PHP Annotations улучшают ваш Laravel проект
- ·
-
С развитием PHP потребность в аннотациях стремительно падает. Раньше типы возвращаемых значений, свойств, параметров и других неочевидных вещей объявлялись аннотациями в комментарии стиля PHPDoc.
На сегодняшний день в текущей версии PHP(8.2) уже можно объявлять типы свойств, параметров, возвращаемых значений методов/функций, есть поддержка типа Enum и других полезные улучшения но, в PHP все так же много осталось «магии» которую с ловкостью используют различные фреймворки. Laravel не исключение. Так как Laravel использует слишком много «магии» порог вхождения в проект для новых разработчиков повышается. По этому в данной статье, мы, с помощью аннотаций, будем понижать тот самый порог.
Для тех кто не знаком что такое аннотации, читайте статью ниже
Модели(Model)
В Laravel для аттрибутов модели используется магические методы PHP __get(), __set(). Взглянем на типичную модель User
class User extends Model
{
protected $table = 'users';
protected $guarded = ['id'];
}
В данной модели User мы видим… ничего… абсолютно никакой информации по атрибутам этой модели. Чтобы узнать атрибуты новому разработчику приходится идти в таблицу, смотреть схему и держать в голове эти знания. Все это занимает время, и не мало, особенно когда моделей много. Но что же делать?! Давайте попробуем применить аннотации
/**
* @property int $id
* @property string $first_name
* @property string $surname
* @property int $age
* @property string $password
* @property bool $active
* @property string $role admin|user
* @property DateTime|null $created_at
* @property DateTime|null $updated_at
*/
class User extends Model
{
protected $table = 'users';
protected $guarded = ['id'];
}
Вот теперь стало лучше. Главное не забывать редактировать аннотации если модифицируете схему. А что на счет методов?! И для методов есть свои аннотации
/**
* @property int $id
* @property string $first_name
* @property string $surname
* @property int $age
* @property string $password
* @property bool $active
* @property string $role admin|user
* @property DateTime|null $created_at
* @property DateTime|null $updated_at
*
* @method static Builder byAge(int $age)
* @method static Builder byActive(bool $active = true)
*/
class User extends Model
{
protected $table = 'users';
protected $guarded = ['id'];
public function scopeByAge(Builder $query, int $age): void
{
$query->where('age', $age);
}
public function scopeActive(Builder $query, bool $active = true): void
{
$query->where('active', $active);
}
}
Вот теперь стало намного лучше. Когда ваши коллеги откроют вашу модель, они сразу увидят аттрибуты и возможные локальные scopes.
Добавим в модель отношений чтобы приблизиться к реальной ситуации.
/**
* @property int $id
* @property string $first_name
* @property string $surname
* @property int $age
* @property string $password
* @property bool $active
* @property string $role admin|user
* @property DateTime|null $created_at
* @property DateTime|null $updated_at
*
* @property Collection<Photo> $photos
* @property Collection<File> $files
* @property Document|null $document
*
* @method static Builder byAge(int $age)
* @method static Builder byActive(bool $active = true)
*/
class User extends Model
{
protected $table = 'users';
protected $guarded = ['id'];
public function photos(): HasMany
{
return $this->hasMany(Photo::class);
}
public function files(): HasMany
{
return $this->hasMany(File::class);
}
public function document(): HasOne
{
return $this->hasOne(Document::class);
}
public function scopeByAge(Builder $query, int $age): void
{
$query->where('age', $age);
}
public function scopeActive(Builder $query, bool $active = true): void
{
$query->where('active', $active);
}
}
Давайте теперь рассмотрим что может быть еще не очевидно в вашем коде что можно улучшить с помощью аннотаций. Допустим у вас есть UserService, с методом getAll() который возвращает массив пользователей.
final class UserService
{
public function getAll(): array
{
return User::all()->toArray();
}
}
Для массивов есть специальный синтасис
final class UserService
{
/**
* @return User[]
*/
public function getAll(): array
{
return User::all()->toArray();
}
}
Теперь станет понятно клиентам вашего сервиса и вашей IDE что находится внутри возвращаемого массива. Для итераторов применяется синтаксис обобщения.
final class UserService
{
/**
* @return Iterator<int, User>
*/
public function getAll(): Iterator
{
return User::all()->getIterator();
}
}
Для кастомных схем массивов тоже есть определенный синтаксис
final class UserService
{
/**
* @return array<array{
* id: int,
* name: string
* }>
*/
public function getAll(): array
{
return User::all()->map(fn (User $user) => [
'id' => $user->getKey(),
'name' => $user->name
])->toArray();
}
/**
* @return array{
* id: int,
* name: string,
* age: int
* }
*/
public function find(int $id): array
{
return User::find($id)->only('id', 'name', 'age');
}
}
В этом примере мы описали схему возвращаемого массива для методов getAll() и find().
Заключение
Аннотации это мощный инструмент который экономит время, помогает отлавливать больше типичных ошибок на этапе разработки и делает ваш код приятней для чтения. Данные аннотации поддерживают практически все современные IDE, и будут вам давать полезные советы при разработке. Аннотации так же используются для автоматического документирования приложения. Вывод один, используйте аннотации и не пренебрегайте этим.