Делимся лучшими практиками Laravel 5.7 и опытом разработки

PHP

file

Laravel хорошо известен большинству PHP-разработчиков тем, что пишет чистый отлаживаемый код. Он также поддерживает множество функций, которые иногда не задокументированы в документации, либо они появились и были удалены по каким-то причинам.

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


Использовать локальную область при запросе данных

У Laravel есть отличный способ использованияпостроитель запросовНапишите запрос. так:

$orders = Order::where('status', 'delivered')->where('paid', true)->get();

очень хорошо. Это позволяет мне сосредоточиться на написании более удобного кода вместо операторов SQL. Но если вы используетелокальная область, мы можем сделать эту строку кода еще лучше.

При запросе данных,локальная областьпозволяют нам создавать свои собственныепостроитель запросовцепной метод. Например, вместо->where(), мы можем использовать более краткий->delivered() а также ->paid().

первый вOrderМодель, мы добавляем несколько методов:

class Order extends Model
{
   ...
   public function scopeDelivered($query) {
      return $query->where('status', 'delivered');
   }
   public function scopePaid($query) {
      return $query->where('paid', true);
   }
}

При объявлении локальной области следует использоватьscope[Something]назвать. Таким образом, Laravel узнает, что это локальная область видимости и может использоваться в построителе запросов. Убедитесь, что вы передаете первый параметр в методе$query, который является экземпляром построителя запросов, автоматически внедряемым Laravel.

$orders = Order::delivered()->paid()->get();

Для запросов, которые принимают дополнительные параметры, вы можете использовать динамическую область видимости. Каждая область позволяет передавать дополнительные параметры.

class Order extends Model
{
   ...
   public function scopeStatus($query, string $status) {
      return $query->where('status', $status);
   }
}
$orders = Order::status('delivered')->paid()->get();

Далее в этой статье вы узнаете, почему следует использовать поля базы данных.蛇形命名, но вот первая причина: Laravel по умолчанию используетwhere[Something]заменитьscope[Something]. Таким образомscopeStatusВместо области действия вы можете сделать:

Order::whereStatus('delivered')->paid()->get();

дляwhere[Something], Laravel будет искать蛇形命名Версия поля базы данных. Если в вашей базе данных естьstatusполе, вы можете использовать приведенный выше пример. если естьshipping_statusполе, вы можете использовать:

Order::whereShippingStatus('delivered')->paid()->get();

вам решать!

Используйте класс запроса, когда это необходимо

Laravel предоставляет отличный способ проверки данных, отправленных из формы. Если вам это нужно, будь то запрос POST или GET, он может проверить.

В контроллере вы можете сделать это:

public function store(Request $request)
{
    $validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body'  => 'required',
    ]);

    // 如果这篇博客的内容无效……
}

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

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

php artisan make:request StoreBlogPost

существуетapp/Http/Requests/Только что созданный класс запроса можно найти в каталоге:

class StoreBlogPostRequest extends FormRequest
{
   public function authorize()
   {
      return $this->user()->can('create.posts');
   }
   public function rules()
   {
       return [
         'title' => 'required|unique:posts|max:255',
         'body' => 'required',
       ];
   }
}

Теперь вы должны использовать только что созданныйApp\Http\Requests\StoreBlogPostRequestВместо оригиналаIlluminate\Http\RequestДобрый:

use App\Http\Requests\StoreBlogPostRequest;

public function store(StoreBlogPostRequest $request)
{
    // 如果这篇博客的内容无效……
}

в классе запросаauthorize()Метод должен возвращать логическое значение. если вернетсяfalse, это вызовет403исключение, пожалуйста, убедитесь, что вы находитесь вapp/Exceptions/Handler.phpизrender()Исключение ловится в методе:

public function render($request, Exception $exception)
{
    if ($exception instanceof \Illuminate\Auth\Access\AuthorizationException) {
        //
    }
    
    return parent::render($request, $exception);
}

В классе запроса есть еще одинmessages()метод, который возвращает массив с сообщениями об ошибках при неудачной проверке:

class StoreBlogPostRequest extends FormRequest
{
   public function authorize()
   {
      return $this->user()->can('create.posts');
   }
	 
   public function rules()
   {
       return [
         'title' => 'required|unique:posts|max:255',
         'body' => 'required',
       ];
   }
	 
   public function messages()
   {
      return [
        'title.required' => 'The title is required.',
        'title.unique' => 'The post title already exists.',
        ...
      ];
   }
}
@if ($errors->any())
   @foreach ($errors->all() as $error)
      {{ $error }}
   @endforeach
@endif

Если вы хотите получить информацию о проверке для поля, вы можете сделать это (когда это поле проверено$errors->has()вернетfalse):

<input type="text" name="title" />
@if ($errors->has('title'))
   <label class="error">{{ $errors->first('title') }}</label>
@endif

магический диапазон

При построении запроса вы можете использовать уже имеющуюся у вас волшебную область:

  • согласно сcreated_atОбратный запрос:
User::latest()->get();
  • Запрос по любому полю в обратном порядке:
User::latest('last_login_at')->get();
  • Случайные запросы (т.е. в операторах SQLORDER BY RAND())
User::inRandomOrder()->get();

Используйте ассоциации вместо длинных запросов (или плохо написанных)

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

пройти черезздесьЗапрос связанных документов.

Используйте систему задач для трудоемких задач

Задачи LaravelЭто мощный инструмент для запуска программ в фоновом режиме.

  • Хотите отправить письмо?Система задач.
  • Вы хотите транслировать сообщение?Система миссий.
  • Хотите обработать картинку?Система квестов.

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

ты сможешьздесьЗапросить документацию по очереди.

При использовании очередей мне нравится использоватьLaravel Horizon, потому что его легко установить, он может работать в фоновом режиме с помощью инструмента Supervisor или файла конфигурации, и я могу указать Horizon, сколько процессов я хочу запустить в каждой очереди.

Соответствие стандартам и средствам доступа к базам данных

Laravel с самого начала учит вас, что переменные и методы должны использоваться как$camelCase camelCase()Такой маленький верблюжий случай и поля базы данных должны быть названы, используя что-то вродеsnake_caseТакое змеевидное имя. Зачем? потому что это помогает нам строить лучшеаксессуар.

Аксессоры — это настраиваемые поля, которые можно создавать непосредственно в модели. Если наша база данных содержитfirst_name,last_name,ageК этим полям мы можем добавить поле с именемnameпроизвольное поле для размещенияfirst_nameа такжеlast_nameСобраны вместе. не волнуйся, этоnameне будет записано в базу данных. Это просто пользовательское свойство какой-то модели. все аксессуары иОбъемОпять же, есть настраиваемые синтаксисы именования:getSomethingAttribute:

class User extends Model
{
   ...
   public function getNameAttribute(): string
   {
       return $this->first_name.' '.$this->last_name;
   }
}

когда используешь$user->name, метод доступа вернет объединенную строку.


По умолчанию сdd($user)не вижуnameатрибут, но через$appendsпеременную мы можем сделать ее всегда доступной:

class User extends Model
{
   protected $appends = [
      'name',
   ];
   ...
   public function getNameAttribute(): string
   {
       return $this->first_name.' '.$this->last_name;
   }
}

теперь каждый разdd($user), мы все можем видетьname. (Но все равно это свойство не выбирается из базы данных, а будетfirst_nameа такжеlast_nameсращенный).

Будьте осторожны, если у вас уже естьnameЭто поле, то ситуация будет немного другой:$appendsв массивеnameЭлемент не нужен, и тогда метод доступа должен передать параметр, который является данными в базе данных.name(То есть нам не нужно использовать$this).

Например, мы можем захотеть использоватьucfirst()сделать заглавной первую букву имени:

class User extends Model
{
   protected $appends = [
      //
   ];
   ...
   public function getFirstNameAttribute($firstName): string
   {
       return ucfirst($firstName);
   }
	 
   public function getLastNameAttribute($lastName): string
   {
      return ucfirst($lastName);
   }
}

Теперь, когда мы используем$user->first_name, который возвращает строку с заглавной первой буквой.

Из-за этой функции поля базы данных лучше всего использовать сsnake_caseЭта форма змеи названа.

Не храните статические данные, связанные с моделью, в файлах конфигурации.

Мне нравится хранить статические данные, связанные с моделью, в файлах модели. Давайте посмотрим вместе.

Не будь таким:

BettingOdds.php

class BettingOdds extends Model
{
   ...
}

config/bettingOdds.php

return [
   'sports' => [
      'soccer' => 'sport:1',
      'tennis' => 'sport:2',
      'basketball' => 'sport:3',
      ...
   ],
];

Для доступа используйте следующие методы:

config('bettingOdds.sports.soccer');

Я предпочитаю делать так:

BettingOdds.php

class BettingOdds extends Model
{
   protected static $sports = [
      'soccer' => 'sport:1',
      'tennis' => 'sport:2',
      'basketball' => 'sport:3',
      ...
   ];
}

Затем получите к ним доступ:

BettingOdds::$sports['soccer'];

почему это так? Потому что это выгодно для последующих операций:

class BettingOdds extends Model
{
   protected static $sports = [
      'soccer' => 'sport:1',
      'tennis' => 'sport:2',
      'basketball' => 'sport:3',
      ...
   ];
   public function scopeSport($query, string $sport)
   {
      if (! isset(self::$sports[$sport])) {
         return $query;
      }
      
      return $query->where('sport_id', self::$sports[$sport]);
   }
}

Теперь мы можем использовать запросы диапазона:

BettingOdds::sport('soccer')->get();

Используйте коллекции вместо обработки примитивных массивов

В прошлом мы обычно использовали массивы примитивным способом:

$fruits = ['apple', 'pear', 'banana', 'strawberry'];
foreach ($fruits as $fruit) {
   echo 'I have '. $fruit;
}

Теперь мы можем использовать расширенный метод (Примечание переводчика: способ сбора) для работы с данными в массиве. Мы можем фильтровать, преобразовывать, повторять и изменять данные в массиве:

$fruits = collect($fruits);
$fruits = $fruits->reject(function ($fruit) {
   return $fruit === 'apple';
})->toArray();
['pear', 'banana', 'strawberry']

Подробнее см.сбор документов.

При использовании построителя запросов->get()метод возвращаетCollectionпример. Но будьте осторожны, чтобы не перепутатьCollection а также Queryстроитель:

  • Из Query Builder мы не можем получить данные. Но у нас есть много методов, связанных с запросами:orderBy()where(),так далее.
  • Последний вызов->get()После метода получаются данные и расходуется место в памяти. он возвращаетCollectionпример. Некоторые построители запросов недоступны или доступны, но с другими именами методов, см.Все методы сбора.

если вы можетеQuery BuilderФильтруйте данные иерархически, просто сделайте это! Не полагайтесь на ожидание результатовCollectionОтфильтруйте, когда экземпляр будет возвращен - вы будете потреблять больше места в памяти. Используйте Limit, чтобы ограничить количество результатов, и используйте индексы на уровне БД, чтобы ускорить запросы.

Эффективно используйте пакеты расширения и не изобретайте велосипед

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

  • Laravel Blade Directives
  • Laravel CORS(При выполнении междоменного запроса ограничьте маршрутизацию доступом к указанному доменному имени)
  • Laravel Tag Helper(проще использовать теги HTML в Blade)
  • Laravel Sluggable(полезно при создании слагов в моделях Eloquent)
  • Laravel Responder(Легче создать JSON API)
  • Image Intervention(обработка фото)
  • Horizon(очередями можно управлять с небольшой настройкой)
  • Socialite(Интеграция сторонних учетных записей социальных сетей с минимальной конфигурацией)
  • Passport(встроенная маршрутизация OAuth)
  • Spatie's ActivityLog(отслеживает действия по модификации модели)
  • Spatie's Backup(резервные файлы и базы данных)
  • Spatie's Blade-X(Определите свои собственные теги HTML; доступно сLaravel Tag Helperкомбинированный)
  • Spatie's Media Library(Быстро связать модели с файлами)
  • Spatie's Response Cache(полный ответ контроллера кеша)
  • Spatie's Collection Macros(Добавить больше макросов в коллекции)

Вот несколько пакетов расширения, которые я (первоначальный автор) написал:

  • Befriended(Аналогично социальным сетям, таким как, прослушивание и блокировка операций)
  • Schedule(создайте расписание и проверьте, доступен ли момент времени)
  • Rating(Добавить функцию оценки в модель)
  • Guardian(Простая в использовании система разрешений)

Слишком сложно понять? Свяжитесь со мной!

Если у вас есть дополнительные вопросы о Laravel, если вам нужна помощь с операциями или вы просто хотите что-то сказать谢谢, ты сможешьTwitter @rennokkiНайди меня!

Перенесено из сообщества разработчиков PHP/LaravelПотяните Ravel-China.org/topics/2216…