Эта статья была рецензирована Кристофером Вунди и Кристофером Питтом . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
Пока я работал над приложением для клиента, мне пришлось реализовать новый модуль, который предполагает следующее:
- Пользователи просят расценки бюджета для определенной задачи.
- Каждое задание имеет свое местоположение.
- Профессионалы могут подписаться на разные зоны.
- Зона может быть областью или городом.
Теперь давайте пренебрегаем основным приложением и попробуем реализовать этот единственный модуль, чтобы увидеть, чего мы можем достичь здесь.
Применение лесов
Я предполагаю, что ваша среда разработки уже настроена. Если нет, то вы можете проверить этот Советы по улучшению Homestead или просто использовать официальную коробку Homestead .
Создайте новый проект Laravel, используя установщик Laravel или Composer.
laravel new demo
Или
composer create-project --prefer-dist laravel/laravel demo
Отредактируйте файл .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
Создание миграций
Прежде чем создавать наши миграции, мы должны поговорить о полиморфных отношениях Eloquent и о том, как они могут принести нам пользу в этом случае!
Полиморфизм часто используется, когда объект может иметь различные формы (формы). В нашем случае профессиональные пользователи подписываются на разные зоны и получают уведомление, когда в этой области публикуется новая работа.
Давайте начнем с создания таблицы zones
php artisan make:model Zone --migration
Это создает файл миграции, но нам нужно добавить немного кода до того, как он будет завершен, о чем свидетельствуют:
// database/migrations/2016_12_02_130436_create_zones_table.php
class CreateZonesTable extends Migration
{
public function up()
{
Schema::create('zones', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('zone_id')->unsigned();
$table->string('zone_type');
});
}
public function down()
{
Schema::dropIfExists('zones');
}
}
Далее мы создаем таблицы городов и регионов.
php artisan make:model Region --migration
// database/migrations/2016_12_02_130701_create_regions_table.php
class CreateRegionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('regions', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('regions');
}
}
php artisan make:model City --migration
// database/migrations/2016_12_02_130709_create_cities_table.php
class CreateCitiesTable extends Migration
{
public function up()
{
Schema::create('cities', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 255);
$table->integer('postal_code')->unsigned();
$table->integer('region_id')->unsigned()->nullable();
});
}
public function down()
{
Schema::drop('cities');
}
}
Мы могли бы достичь того же результата, установив связь «многие ко многим» с таблицей городов и регионов. Однако таблица зон будет действовать как абстрактный класс для двух других таблиц. Идентификатор zone_id
zone_type
Создание моделей
Eloquent имеет возможность угадывать связанные поля таблицы, но я решил не использовать его просто для объяснения того, как сопоставить поля базы данных с моделями. Я объясню их по ходу дела!
// app/User.php
class User extends Authenticatable
{
// ...
public function cities()
{
return $this->morphedByMany(City::class, 'zone', 'zones', 'user_id', 'zone_id');
}
public function regions()
{
return $this->morphedByMany(Region::class, 'zone', 'zones', 'user_id', 'zone_id');
}
}
Метод morphedByMany
manyToMany
Мы указываем связанную модель, имя поля отображения (используется для zone_type
zone_id
Мы могли бы автоматизировать это, позволив Eloquent угадать имена полей, если вы посмотрите на документацию, то увидите, что мы можем назвать поля как zoneable_id
zoneable_type
return $this->morphedByMany(City::class, 'zoneable'
// app/Region.php
class Region extends Model
{
// ...
public function cities()
{
return $this->hasMany(City::class);
}
public function users()
{
return $this->morphMany(User::class, 'zones', 'zone_type', 'zone_id');
}
}
Мы можем быстро угадать определение параметров из приведенного выше кода. Мы указываем имя связанной таблицы, вид измененного отношения и идентификатор.
// app/City.php
class City extends Model
{
// ...
public function users()
{
return $this->morphMany(User::class, 'zones', 'zone_type', 'zone_id');
}
}
Теперь, когда все настроено, мы можем начать тестировать наши отношения и посмотреть, все ли работает так, как ожидалось. Мы должны сначала посеять таблицы, чтобы сэкономить время. Вы можете получить код получателя базы данных из репозитория GitHub .
Использование отношений
Мы можем присоединять города и регионы к пользователям, используя синхронизацию, присоединение и отключение методов Eloquent. Вот пример:
$user = App\User::find(1);
$user->cities()->sync(App\City::limit(3)->get());
Это прикрепит три города к выбранному пользователю, и мы можем сделать то же самое для регионов.
$user = App\User::find(1);
$user->regions()->sync(App\Region::limit(3)->get());
Если мы проверим нашу базу данных сейчас, чтобы увидеть, что было сохранено, мы увидим следующее:
mysql> select * from zones;
+---------+---------+------------+
| user_id | zone_id | zone_type |
+---------+---------+------------+
| 1 | 1 | App\City |
| 1 | 2 | App\City |
| 1 | 3 | App\City |
| 1 | 1 | App\Region |
| 1 | 2 | App\Region |
| 1 | 3 | App\Region |
+---------+---------+------------+
6 rows in set (0.00 sec)
Мы также можем отделить регионы, если они существуют.
$user = App\User::find(1);
$user->regions()->detach(App\Region::limit(3)->get());
Теперь мы можем выбирать пользовательские города и регионы, как если бы у нас были нормальные отношения «многие ко многим»:
$user = App\User::find(1);
dd($user->regions, $user->cities);
Мы могли бы добавить новый метод под названием zones
class User extends Authenticatable
{
// ...
public function zones()
{
return $this->regions->pluck("cities")->flatten()->merge($this->cities);
}
}
Метод pluck позволяет получить коллекцию городов из каждого региона, которые затем объединяются (объединяются все коллекции в один) и объединяются с выбранными пользователем городами. Вы можете прочитать больше о коллекциях в документации, и если вы хотите узнать больше, я рекомендую эту книгу Refactoring to Collections от Adam Wathan.
Вывод
Несмотря на то, что полиморфные отношения редко используются в приложениях, Eloquent позволяет легко работать со связанными таблицами и возвращать правильные данные. Если у вас есть какие-либо вопросы или комментарии о Eloquent или Laravel в целом, вы можете опубликовать их ниже, и я сделаю все возможное, чтобы ответить на них!