Laravel

Ошибка Undefined index: name в файле PackageManifest. php при установке зависимостей Composer

Недавно при установке Laravel-приложения столкнулся с ошибкой:

In PackageManifest.php line 122:
Undefined index: name 

Судя по гуглу, проблема существует последние полгода. Связано это с выходом второй версии композера, а также с регулярными обновлениями ядра Laravel.

Сначала попробуйте починить с помощью обновления композера до стабильной версии:

composer self-update --stable

Если это не поможет, то попробуйте откатиться до первой версии композера:

sudo composer self-update --1

В моём случае сработал откат до первой версии.

Как в Blade вывести переменную Vue

Иногда возникает необходимость написать что-то вроде {{ $route.name }} внутри blade-шаблона в Laravel-приложении. Laravel тут же поругается, что такой переменной нет. Выход такой:

<div class="custom-block">
    @{{ $route.name }}
    ...
</div>

Используйте синтаксис @{{ }} для такой задачи.

Как привязать чат-бота в Telegram к текущему пользователю в Laravel

В большинстве инструкций по работе с телеграм-ботом не сказано про то, как получить тот самый RECIPIENT_CHAT_ID, в который надо слать сообщения из PHP.

Вот типичный пример отправки сообщения из Laravel-приложения в Телеграм:

Route::post('bot/sendmessage', function() {
    Telegram::sendMessage([
        'chat_id' => 'RECIPIENT_CHAT_ID',
        'text' => 'Привет, мир!'
    ]);
    return;
});

Предположим, что наша задача — слать уведомления пользователю в телеграм, если на сайте ему ответили на комментарий. Такое уведомление нужно отправлять только конкретному пользователю, а не всем сразу.

Простой способ для привязки пользователя на сайте и чата с ботом в телеграме — использование ссылки-инвайта для добавления чат-бота. Вы даёте пользователю специальную ссылку с параметром, и когда он на неё кликает — открывается чат с ботом, бот получает инструкцию «начать работу» и отправляет информацию на сервер вместе с тем самым параметром из ссылки, а также CHAT_ID. По этому параметру вы находите в своей базе пользователя и назначаете ему CHAT_ID.

Показываем пользователю на сайте ссылку:

https://telegram.me/arutyunov_bot?start=asd123

Когда пользователь перейдёт по ссылке, у него откроется чат 1-на-1 с ботом. Как только пользователь нажмёт кнопку Start, бот получит сообщение вида:

/start PAYLOAD

Вместо PAYLOAD и будет тот самый токен. В нашем примере это asd123. Теперь вам нужно обработать это сообщение на своём сервере.

Вот пример для обработчика, который привязан к Webhook:

if ($message->getText()) {
    if (strpos('/start', $message->getText()) !== false) {
        $textStrings = explode(' ', $message->getText());

        if (isset($textStrings[1])) {
            $token = $textStrings[1];
            $chatId = $message->getChat()->getId();

            if ($token) {
                $user = User::find(['telegram_token' => $token]);

                if ($user !== null) {
                    $user->telegram_chat_id = $chatId;
                    return response()->json(['status' => $user->save()]);
                }
            }
        }
    }
}

В официальной документации Телеграм есть описание этого механизма, который называется Deep linking.

В телеграме каждый чат имеет свой уникальный CHAT_ID. Обратите внимание, что чат с ботом будет иметь числовой CHAT_ID больше 0, а если вы добавили бота в группу, то CHAT_ID будет отрицательный.

Готовый код для Pagination в Bulma для Laravel 5

Сначала вам нужно скопировать к себе в resources/views шаблон пагинации. Делаем:

php artisan vendor:publish --tag=laravel-pagination

Эта команда разместит шаблоны в папке resources/views/vendor/pagination.

По-умолчанию Laravel использует шаблон bootstrap-4.blade.php. Меняем его код:

@if ($paginator->hasPages())
    <nav class="pagination is-centered" role="navigation" aria-label="pagination">
        {{-- Previous Page Link --}}
        @if ($paginator->onFirstPage())
            <a class="pagination-previous" disabled>Previous</a>
        @else
            <a class="pagination-previous" href="{{ $paginator->previousPageUrl() }}">Previous</a>
        @endif

        {{-- Next Page Link --}}
        @if ($paginator->hasMorePages())
            <a class="pagination-next" href="{{ $paginator->nextPageUrl() }}">Next Page</a>
        @else
            <a class="pagination-next" disabled>Next Page</a>
        @endif
        

        {{-- Pagination Elements --}}
        <ul class="pagination-list">
            @foreach ($elements as $element)
                {{-- "Three Dots" Separator --}}
                @if (is_string($element))
                    <li><span class="pagination-ellipsis">…</span></li>
                @endif

                {{-- Array Of Links --}}
                @if (is_array($element))
                        @foreach ($element as $page => $url)
                            @if ($page == $paginator->currentPage())
                                <li><a class="pagination-link is-current" aria-label="Goto page {{ $page }}">{{ $page }}</a></li>
                            @else
                                <li><a href="{{ $url }}" class="pagination-link" aria-label="Goto page {{ $page }}">{{ $page }}</a></li>
                            @endif
                        @endforeach
                @endif
            @endforeach
        </ul>

    </nav>
@endif

Как посмотреть размер всех подключаемых через Laravel-mix Javascript библиотек

Бывает полезно посмотреть размер файлов всех подключаемых библиотек и фреймворков, из которых собирается приложение. Это можно легко сделать с помощью плагина bundle-analyzer для Webpack. Ставим плагин:

npm i laravel-mix-bundle-analyzer

Добавляем плагин в webpack.mix.js:

const mix = require('laravel-mix');
require('laravel-mix-bundle-analyzer');

if (!mix.inProduction()) {
    mix.bundleAnalyzer();
}

Теперь вы можете запустить npm run dev, и после сборки откроется браузер с анализом:

Результат работы Bundle-analyzer

Полная документация к плагину доступна на сайте laravel-mix.

Плюрализация строк в Laravel для русского языка

Часто встречается задача выбора окончания для строк во множественном числе. В Laravel есть готовое решение, которое описано в документации, но мне не удалось найти готового примера, который бы работал корректно с русским языком. Вот простое решение, которое покрывает большинство случаев:

{{ trans_choice('Публикация|Публикации|Публикаций', $user->articles->count()) }}

В редких ситуациях могут возникать нюансы, которые нужно решать с помощью уточнения числовых промежутков. Об этом подробнее читайте в документации.

Ошибка 419 при авторизации в Laravel

Столкнулся с проблемой возникновения 419 ошибки при попытке авторизации в приложении Ларавел.

Во-первых, стоит проверить CSRF: в форме, в мета-тегах, в скриптах.

У меня с CSRF всё было в порядке — в форме есть, в мета-тегах есть, но ошибка всё-равно возникает. Ларавель пишет «Whoops, something went wrong».

Проблема оказалась в том, что у меня была подключена админка, в которой тоже есть авторизация (мы использовали Backpack на проекте). Нужно в конфигах перенастроить гарды, чтобы всё работало как надо без ошибок:

'guards' => [
	'web' => [
		'driver' => 'session',
		'driver_prefix' => 'user_',
		'provider' => 'users',
	],
	'admin' => [
		'driver' => 'session',
		'driver_prefix' => 'admin_',
		'provider' => 'admins',
	],
],

Обратите внимание на префиксы. Раньше их не было, и сессия хранилась только одна и для приложения, и для админки. После такой настройки всё стало работать.

Не забудьте после изменения конфигов сбросить кеш:

php artisan config:cache

Как получить Email пользователя при авторизации Вконтакте через Laravel Socialite

Стандартный способ получения эл. почты в Laravel Socialite не работает для Вконтакте:

$userSocial = Socialite::driver('vkontakte')->user();
$user = new User;
$user->name = $userSocial->name;
$user->email = $userSocial->email;
$user->save();

Это происходит потому, что Вконтакте возвращает ответ в нестандартном формате JSON. Вот так можно получить почту пользователя:

public static function getUserEmail($user, $provider)
{
    if($provider == 'vkontakte') {
        return $user->accessTokenResponseBody['email'] ?? null;
    }
    return $user->email;
}