Советы по Laravel. Часть 3.

Советы по Laravel для новичков

Laravel полон скрытых жемчужин, недокументированных или малоизвестных функций, опций и «хаков». Все что я нашёл, за время своей работы, я оформил в отдельную статью. Это третья часть, а начало здесь: Советы по Laravel. Часть 1 и Советы по Laravel. Часть 2.

Совет 41. Когда (НЕ НУЖНО) запускать «composer update»

Не совсем о Laravel, но… Никогда не запускайте composer update на боевом сервере — это медленно и может «сломать» репозиторий. лучше это сделать локально на вашем компьютере и отправить новый composer.lock в репозиторий и только после этого запустить composer install на сервере.

 

Совет 42. Двухуровневая переменная $loop в Blade

В Блейдовском foreach вы можете использовать переменную $loop даже в двухуровневом цикле для получения родительской переменной.

@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            This is first iteration of the parent loop.
        @endif
    @endforeach
@endforeach

 

Совет 43. Как избежать ошибки в {{ $post->user->name }}, если пользователь удален?

Вы можете назначить модель по умолчанию в отношениях belongsTo, чтобы избежать фатальных ошибок при ее вызове, например: {{ $post->user->name }}, если $post->user не существует.

/**
 * Получить автора сообщения
 */
public function user()
{
    return $this->belongsTo('App\User')->withDefault();
}

 

Совет 44. Загрузка массива шаблонов

Вы можете загружать шаблоны Blade из массива при помощи view()->first($array), это загрузит только первый существющий. Полезно, когда у вас может быть пользовательский файл, не сгенерированный для одной из «тем», подстрахованный дефолтным вариантом.

return view()->first(['custom.admin', 'admin'], $data);

 

Совет 45. Привязка маршрута к модели: Определите ключ

Вы можете привязать маршрут к модели, например так:

Route::get('api/users/{user}', function(App\User $user) { ... }

и но не только по полю ID. Если вы хотите, чтобы {user} использовал поле username, то сделайте в модели:

public function getRouteKeyName() {
    return 'username';
}

 

Совет 46. Перенаправление на определенный метод контроллера

Вы можете сделать redirect() не только на URL или конкретный маршрут, но и на конкретный метод конкретного контроллера, и даже передать параметры. Используйте это:

return redirect()->action('SomeController@method', ['param' => $value]);

 

Совет 47. А вы знаете об Auth::once()?

Вы можете залогинить пользователя только на один запрос, используя метод Auth::once(). Ни сессии, ни куки использоваться не будут, что подразумевает, этот метод может быть полезен при создании API без сохранения состояния (stateless API).

if (Auth::once($credentials)) {
    //
}

 

Совет 48. Жадная загрузка с точными полями

Вы можете использовать в Laravel жадную загрузку (Eager Loading) и указать какие точно поля вы хотите получить из отношений.

$users = App\Book::with('author:id,name')->get();

 

Совет 49. Проверяйте даты словами «now» или «yesterday»

Вы можете провести валидацию дат правилами до и после (before/after) и задавать различные параметр, например: «tomorrow» (завтра), «now» (сегодня), «yesterday» (вчера). Например: ‘start_date’ => ‘after:now’. Под капотом у этого используется функция strtotime().

$rules = [
    'start_date' => 'after:tomorrow',
    'end_date' => 'after:start_date'
];

 

Совет 50. Легко обновляем родительское updated_at

Если вы обновляете запись и хотите обновить поле updated_at в родительском отношении (например, вы добавляете новый комментарий и хотите обновить posts.updated_at), просто используйте свойство $touches = [‘post’]; в дочерней модели.

class Comment extends Model
{
    /**
     * Обновляем все отношения
     *
     * @var array
     */
    protected $touches = ['post'];
}

 

Совет 51. Быстрая навигация из Маршрутов в Контроллер

Вместо такого маршрута:

Route::get('page', 'PageController@action');

Вы можете указать контроллер как класс:

Route::get('page', [\App\Http\Controllers\PageController::class, 'action']);

После этого вы сможете нажать «PageController» в PhpStorm и перейти непосредственно в Контроллер, вместо того, чтобы искать его вручную.

 

Совет 52. Всегда проверяйте, существует ли Отношение

Никогда не используйте $model->relationship->field без проверки, существует ли объект отношений. Он может быть удален по любой причине, вне вашего кода, кем-то другим по задаче и т. д. Выполните if-else или {{ $model->relationship->field ?? » }} в Blade

 

Совет 53. Не фильтруйте по NULL в Коллекциях

Вы можете фильтровать по NULL в Eloquent, но если вы фильтруете коллекцию — делайте это по пустой строке, в этом поле больше нет нуля.

// Это работает
$messages = Message::where('read_at is null')->get();

// Не работает - вернет 0 сообщений
$messages = Message::all();
$unread_messages = $messages->where('read_at is null')->count();

// Это работает
$unread_messages = $messages->where('read_at', '')->count();

 

Совет 54. Дефолтная тема письма в уведомлениях Laravel

Если вы отправляете Laravel Notification и не указываете тему в toMail(), то темой становится имя нотификационного класса с переводом ВерблюжьегоРегистра в пробелы.

Итак, если у вас есть:

class UserRegistrationEmail extends Notification { // ...

То вы получите электронное письмо с темой «User Registration Email»

 

Совет 55. Composer: проверка новых версий

Если вы хотите узнать, у каких из ваших пакетов в composer.json вышли новые версии, просто запустите «composer outdated». Вы получите полный список со всей информацией, например:

phpdocumentor/type-resolver 0.4.0 0.7.1
phpunit/php-code-coverage 6.1.4 7.0.3 Library that provides collection, processing, and rende...
phpunit/phpunit 7.5.9 8.1.3 The PHP Unit Testing framework.
ralouphie/getallheaders 2.0.5 3.0.3 A polyfill for getallheaders.
sebastian/global-state 2.0.0 3.0.0 Snapshotting of global state

 

Совет 56. Резервный маршрут — когда не найдено никакого другого маршрута

Если вы хотите указать дополнительную логику для отсутствующих маршрутов, то вместо того, чтобы выбрасывать 404-ую страницу, вы можете создать для неё специальный маршрут, разместив его самым последним:

Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
    Route::get('/home', 'HomeController@index');
    Route::resource('tasks', 'Admin\TasksController');
});

// Еще какие-то маршруты....

Route::fallback(function() {
    return 'Хм… Почему ты оказался здесь?';
});

 

Совет 57. Создайте свою собственную Blade команду

Это очень просто — добавьте свой собственный метод в app/Providers/AppServiceProvider.php:
Например, если вы хотите заменять <br> на перевод строки:

<textarea>@br2nl($post->post_text)</textarea>

Добавьте её создание в метод boot() в AppServiceProvider:

public function boot()
{
    Blade::directive('br2nl', function ($string) {
        return "<?php echo preg_replace('/\<br(\s*)?\/?\>/i', \"\n\", $string); ?>";
    });
}

Совет 58. Используйте withCount() для подсчета дочерних записей

Если у вас есть отношение hasMany() и вы хотите подсчитать количество «дочерних» записей, не нужно писать дополнительный запрос.
Например, если у вас есть записи и комментарии в модели User, сделайте с помощью withCount():

public function index()
{
    $users = User::withCount(['posts', 'comments'])->get();
    return view('users', compact('users'));
}

А затем, в файле Blade, вы получите доступ к этим числам при помощи свойства [relationship]_count:

@foreach ($users as $user)
    <tr>
        <td>{{ $user->name }}</td>
        <td class="text-center">{{ $user->posts_count }}</td>
        <td class="text-center">{{ $user->comments_count }}</td>
    </tr>
@endforeach

 

Совет 59. Используйте groupBy для коллекций с пользовательской функцией обратного вызова

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

$users = User::all()->groupBy(function($item) {
    return $item->created_at->format('Y-m-d');
})

Обратите внимание: это делается в Коллекции, поэтому выполняется ПОСЛЕ того, как результаты получены из базы данных.

 

Совет 60. Команды Blade: IncludeIf, IncludeWhen, IncludeFirst

Если вы не уверены, существует ли шаблон Blade, то вы можете использовать следующие команды:

Загрузит заголовок, если шаблон Blade существует
@includeIf(‘partials.header’)

Загрузит заголовок только для пользователя с role_id равным 1
@includeWhen(auth()->user()->role_id == 1, ‘partials.header’)

Попытается загрузить adminlte.header, если не найдет, то загрузит default.header
@includeFirst(‘adminlte.header’, ‘default.header’)

 

Автор: Povilas Korop
Перевод: Demiurge Ash