Laravel 4.2 Модели (часть 5)
Все модели расположены в каталоге /app/models
и расширяют класс Eloquent
(подробнее о Eloquent
речь пойдет в следующей статье). По умолчанию Laravel ожидает, что имя класса модели должно соответствовать единственному числу имени сущности (таблицы), например user
→ users
. Но вы можете именовать и таблицы в единственном числе, указав в модели правильное имя таблицы.
Давайте создадим классы моделей User
и Role
, на основе которых я поведу повествование дальше. Рассмотрим случай при котором пользователь может иметь несколько ролей, т.е. присутствует связь Many-to-Many
.
# app/models/User.php
/**
* @property string $title Не стесняйтесь декларировать magic свойства (поля таблицы)
* @property \Illuminate\Database\Eloquent\Collection|\Roles[] $roles
* @property object $some_field
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
*
* @method static \User firstOrFail(array $columns=[])
* @method static \Illuminate\Support\Collection|\User findOrFail(mixed $id, array $columns=['*'])
* @method static \User whereName(string $username)
*/
class User extends Eloquent
{
// Для активации Soft Delete теперь используется trait вместо свойства `$softDelete`
// Трэйт не может применяться к базовой модели! Только на реальной модели
use SoftDeletingTrait;
// Имя соединения с БД
protected $connection = 'remote_dev';
// Имя таблицы
protected $table = 'user';
// Если имя PK таблицы не id
protected $primaryKey = 'id';
// Разрешить массовое присваивание значений для этих полей
protected $fillable = ['username', 'email', 'is_active'];
// Запретить mass assigned для этих полей
protected $guarded = ['id'];
// Скрыть эти поля при экспорте методами toArray(),toJSON()
protected $hidden = ['id', 'password'];
// Mutated атрибуты, которые нужно выводить в toArray()
protected $appends = ['accessor_field'];
// Отключить использование timestamp полей
public $timestamps = false;
protected $dates = ['logged_at', 'deleted_at'];
// Вызывается при инициализации модели
public static function boot()
{
parent::boot();
// Задать класс-наблюдатель, @see http://bit.ly/1kIexuO
static::observe(new UserObserver());
// Пример биндинга события (перед созданием)
self::creating('User@generateAuthFields');
self::saving([get_class(), 'onSavingSomeAction']);
}
public static function generateAuthFields($user)
{
if (!$user->uid) {
$user->uid = md5(uniqid($user->email, true));
}
$user->token = md5(uniqid(rand(), true));
$user->password = Hash::make($user->password);
}
public static function onSavingSomeAction($user) {}
// Список полей, которые содержат дату (timestamp, datetime)
// Эти поля будут преобразованы к типу Carbon\Carbon
// @see https://github.com/briannesbitt/Carbon
public function getDates()
{
return ['created_at', 'updated_at', 'active_to'];
}
// Формат даты и времени по умолчанию
// При установке дефолтного формата для даты, вы можете столкнуться
// с ошибкой: "Trailing data" - я долго не разбирался и удалил этот метод.
protected function getDateFormat()
{
return 'Y-m-d H:i:s';
}
// ~ ~ ~ АКСЕССОРЫ, МУТАТОРЫ (accessor, mutator)
// Выполняется при получении свойства модели (даже несуществующего)
public function getSomeFieldAttribute($current_value) {
// Можете вернуть значение другого поля: $this->other;
return json_decode($current_value);
}
// Выполняется при установке свойства, в т.ч. методами create(), update(), fill()
public function setSomeFieldAttribute($new_value) {
$this->attributes['some_field'] = json_encode($new_value);
}
// ~ ~ ~ HELPERS
public static function getOptions() {
return self::orderBy('name')->get()->lists('name', 'id');
}
// ~ ~ ~ RELATIONS
/**
* Описание связи много ко многим
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function roles()
{
// (!) Имена связующих полей `user_id` и `role_id` будут генериться автоматически,
// по имени таблицы + окончание `_id`. Явно указывать эти поля необходимо лишь в случае,
// если имя поля не соответствует этому формату, например имени таблицы.
// (!) withPivot - извлечь доп. поля из связующей (relation, pivot) таблицы.
// Эти поля будут доступны в свойстве pivot при экспорте (toArray, toJSON)
// (!) withTimestamps - обновлять timestamp поля в pivot таблице при добавлении связи
return $this->belongsToMany('Role', 'user_roles', 'user_id', 'role_id')
->withTimestamps() // указываем, что есть timestamp поля
->whereNull('user_group.deleted_at') // Кастомный фильтр (эмуляция Soft Delete)
->withPivot('created_at', 'updated_at', 'some_field');
}
/**
* One To Many связь (юзер имеет много постов)
* $user_posts = $user->posts;
* $user_posts = $user->posts()->where(...)->first();
* @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
*/
public function posts()
{
return $this->hasMany('Post', 'user_id');
}
public function getRolesIds()
{
return $this->roles()->get()->modelKeys();
}
public function getChildren()
{
$ids = TableRelation::where('parent_id', $this->tableRelation->id)->lists('user_id');
if (count($ids)) {
return User::find($ids);
}
// Вернуть пустую коллекцию
return new \Illuminate\Database\Eloquent\Collection([]);
}
}
Модель Role
:
# app/models/Role.php
class Role extends Eloquent
{
public function someModels()
{
return $this->hasManyThrough('Model1', 'Model1', 'id', 'model_id');
}
}
Модель Post
:
class Post extends Eloquent
{
public function author()
{
// Связь Many-to-One
return $this->belongsTo('User', 'user_id'); // post.user_id = user.id
}
public function author()
{
// Связь One-to-One
return $this->hasOne('Meta', 'post_id'); // post.id = meta.post_id
}
}
#Model, #Eloquent