Laravel / PHP - Расширенные полиморфные отношения - PullRequest
4 голосов
/ 23 апреля 2019

В моем веб-приложении пользователи могут загружать documents или emails в channels.

Кроме того, канал может иметь document_tags и email_tags, что всех загруженных документов / электронных писем автоматически должно наследоваться.

Кроме того, document_tags и email_tags будут иметь различные описания: tag_descriptions. Например, если у нас есть документ, загруженный на канал с тегами: animals (id = 1) и pets (id = 2)

  1. Document #55 преобразуется в Channel #8.
  2. Document #55 автоматически унаследует теги, которые имеют document_tags.channel_id = 55 (к нему можно получить следующие отношения: $channel->documenttags). В этом случае animals и pets.
  3. Теперь пользователь должен иметь возможность задать уникальное описание тегов animals и pets в tag_descriptions, например:

tag_descriptions

id | taggable_type   | taggable_id  | typeable_type | typeable_id | description
1  | App\DocumentTag | 1            |  App\Document | 55          | My unique description for animals. 
2  | App\DocumentTag | 2            |  App\Document | 55          | My unique description for pets.

Теперь в приведенной выше схеме базы данных загруженные document #55 имеют связанные теги: animals и pets, но далее эти два тега имеют уникальное описание, уникальное для конкретного документа .

Если я загружу другой документ или электронное письмо (скажем, email #20), то я думаю, что это будет выглядеть так:

tag_descriptions

id | taggable_type   | taggable_id  | typeable_type | typeable_id | description
1  | App\DocumentTag | 1            |  App\Document | 55          | My unique description for animals. 
2  | App\DocumentTag | 2            |  App\Document | 55          | My unique description for pets.
3  | App\EmailTag    | 1            |  App\Email    | 20          | Another unique description for animals. 
4  | App\EmailTag    | 2            |  App\Email    | 20          | Yet another unique description for pets.

Теперь email #20 также имеют теги animals и pets, но в этом случае пользователь может задать уникальные описания для тегов.

Теперь мой вопрос:

Реализуется ли выше дизайна, и считается ли это лучшей практикой в ​​Laravel / PHP? Я немного не уверен, как структурировать код, потому что модель TagDescription внезапно будет иметь два полиморфных отношения (taggable и typeable), и я не могу найти ничего в документации, что это поддерживается.

Кроме того, я не уверен, что смогу использовать вышеуказанный дизайн для доступа к уникальным описаниям через определенный загруженный документ, например:

//In my Document.php model:
public function tagdescriptions()
{
    return $this->morphMany(TagDescription::class, 'typeable');
}

Тогда используйте это как: $document->tagdescriptions.

Последнее, но не менее важное: я немного не уверен, как сохранить описание уникального тега для конкретного тега taggable_id / taggable_type и уникального электронного письма / документа. (typeable_id и typeable_type).

1 Ответ

1 голос
/ 03 мая 2019

Я не уверен, что именно вы пытаетесь сделать, но таблица с двумя полиморфными отношениями не имеет смысла. Таблица, которая включает полиморфное отношение, является сводной таблицей. Хотя я получаю, что вам нужны уникальные описания для каждого тега и типа связи, сводная таблица должна иметь только два столбца внешнего ключа, один из которых относится к таблице.
Есть еще один способ, который заключается в использовании полиморфных отношений в качестве ограничения на сводную таблицу. Для начала с сводной таблицей в полиморфных отношениях следует переименовать ее в «taggables». Вам не нужны таблица «email_tag» и «document_tag», вы можете использовать таблицу с именем «tags». Чтобы получить уникальное описание для каждого тега, вы можете добавить описание в таблицу «tabblables».

Файл миграции выглядит следующим образом ....

public function up()
{
    Schema::create('taggables', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('tag_id');
        $table->unsignedInteger('taggable_id');
        $table->string('taggable_type');
        $table->string('description');
        $table->timestamps();
    });

    Schema::create('tags', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });

    Schema::create('documents', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });

    Schema::create('emails', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });
}

Вот, что вам нужно сделать в моделях электронной почты и документов.

class Email extends Model
{
    /**
     * Get all of the tags.
     */
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable')->withPivot('description');
    }
}

class Document extends Model
{
    /**
     * Get all of the tag descriptions.
     */
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable')->withPivot('description');
    }
}

Функция «withPivot» возвращает значение столбца, указанного в запросе.

Вот что вам нужно сделать в вашей модели тегов.

class Tag extends Model
{
    /**
     * Get all of the tag descriptions.
     */
    public function documents()
    {
        return $this->morphByMany(Document::class, 'taggable');
    }

    /**
     * Get all of the tag descriptions.
     */
    public function emails()
    {
        return $this->morphByMany(Email::class, 'taggable');
    }
}

Вам не нужна табличная модель Taggables.

Вот что происходит. Когда вы повозитесь ...

$email = App\Email::find(1)->tags;

Этот запрос будет выполнен ...

select `tags`.*, 
    `taggables`.`taggable_id` as `pivot_taggable_id`, 
    `taggables`.`tag_id` as `pivot_tag_id`, 
    `taggables`.`taggable_type` as `pivot_taggable_type`, 
    `taggables`.`description` as `pivot_description` 
from `tags` 
inner join `taggables` on `tags`.`id` = `taggables`.`tag_id` 
where `taggables`.`taggable_id` = 1 
and `taggables`.`taggable_type` = 'App\Email'

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

Я думаю, что добавить этот файл AppServiceProvider.php выглядит чище ...

public function boot()
{
    Relation::morphMap([
        'email'     => Email::class,
        'document'  => Document::class
    ]);
}

https://laravel.com/docs/5.8/eloquent-relationships#polymorphic-relationships

Это позволит вам сохранить значение «email» или «document» в качестве типа тега.

Для публикации в таблицах я думаю, что это выглядит самым чистым ...

$tag = Tag::firstOrNew(["name"=>"#tag"]);
$tag->name = "#tag";

$document = new Document();
$document->name = "new doc";
$document->save();
$document->tags()->save($tag,["description"=>"some description"]);

Вы, конечно, можете использовать attach (). Save () использует метод attach (), как показано здесь ... https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php#L871

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...