How do I do Polymorphic Relations between addons
Created 7 years ago by endderSo I have a Tags addon which creates relations in many other addons like: releases addon, videos addon, documents addon, images addon, etc.
I used the multiple field type (anomaly.field_type.multiple) in my migrations for my releases, videos, documents, images, addons and it all work great on the frontend.
{#This work prefect#}
{% for release in releases %}
<li> {{ release.title }}</li>
<ul>
{% for tag in release.tags %}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>
{% endfor %}
but I want to be able to retrieve the reverse relate via tags. example below:
{#This doesn't work#}
{% for tag in tags %}
<ul>
{% for article in tag.articles %}
<li>{{ article.title }}</li>
{% endfor %}
</ul>
<ul>
{% for image in tag.images %}
<li>{{ image.title }}</li>
{% endfor %}
</ul>
<ul>
{% for video in tag.videos %}
<li>{{ video.title }}</li>
{% endfor %}
</ul>
{% endfor %}
I did try adding a morphedByMany method to my TagModel class but that didn't seem to work. example below:
class TagModel extends TagsTagsEntryModel implements TagInterface
{
public function releases()
{
return $this->morphedByMany('Anomaly\ReleasesModule\Release\ReleaseModel', 'default_releases_releases_tags', 'entry_id', 'related_id');
}
}
Thanks in advance
@piterden find below my tags migrations and releases migrations
Tags
<?php
use Anomaly\Streams\Platform\Database\Migration\Migration;
class AnomalyModuleTagsCreateTagsFields extends Migration
{
/**
* The addon fields.
*
* @var array
*/
protected $fields = [
'name' => [
'type' => 'anomaly.field_type.text'
],
'slug' => [
'type' => 'anomaly.field_type.slug',
'config' => [
'type' => '-',
'slugify' => 'name'
]
]
];
}
Releases
<?php
use Anomaly\Streams\Platform\Database\Migration\Migration;
use Anomaly\TagsModule\Tag\TagModel;
class AnomalyModuleReleasesCreateReleasesFields extends Migration
{
/**
* The addon fields.
*
* @var array
*/
protected $fields = [
'title' => 'anomaly.field_type.text',
'slug' => [
'type' => 'anomaly.field_type.slug',
'config' => [
'type' => '-',
'slugify' => 'title'
]
],
'excerpt' => 'anomaly.field_type.textarea',
'content' => 'anomaly.field_type.wysiwyg',
'state' => [
'type' => 'anomaly.field_type.boolean',
'config' => [
"mode" => "switch",
"on_text" => "Live",
"off_text" => "Halt",
]
],
'published_at' => [
'type' => 'anomaly.field_type.datetime'
],
'tags' => [
"type" => "anomaly.field_type.multiple",
"config" => [
'related' => TagModel::class
]
]
];
}
Look,
You have create the migrations, then when you runs Streams Platform
generates models depending on your migrations.
If you set up in your migration of releases
(for example) and set to it field tags, which is anomaly.field_type.multiple
, then you should see that tags()
method is reveal itself inside generated model
storage/streams/{app_reference}/models/{slug}{namespace}sEntryModel.php
But there's no method releases
inside TagsEntryModel
. If you want to add such, you have to create this method manually, but not in the model, generating by the Streams
. You'd better not edit that files anyway) But you'd create method inside your model which extends automatic generated.
So what we have at the finish:
-
Field config in
fields
section ofmodule migration
here we sets just a listing of all fields you going to use in all streams of exactly this addon, together//..... 'tags' => [ "type" => "anomaly.field_type.multiple", // never use mixed quotes, just in case) "config" => [ 'related' => TagModel::class, 'mode' => 'lookup', // you should try this... ] ], //.....
also we need to set up each
stream migration
withassignments
andstream
config: columns in tables depends exactly on theassignments
array, but this is not straight depending, don't forget itprotected $stream = [ 'slug' => 'releases', 'title_column' => 'name', 'translatable' => true, 'searchable' => true, 'trashable' => true, 'sortable' => true, ]; protected $assignments = [ 'name' => [ 'required' => true, 'unique' => true, ], 'tags', // actually you need only name here, but you may set config too, if you use same field for different streams ];
then, after installing module you need to open Anomaly/TagsModule/Tags/TagsModel and add to it:
public function releases() { return $this->belongsToMany(ReleaseModel::class, 'default_pivot_table_name', 'enty_id', 'related_id'); // not 100% sure, but like this - look at docs of Laravel }
@piterden the relations is now work! 😄. Thanks for your feedback.
But the only way I could get them to work was to add public function releases
to TagsTagsEntryModel
class (which i know you said was a big no no), but it seems on the frontend when I foreach my tags, they are an instance of TagsTagsEntryModel
and not 'TagModel`.
Is this a bug or am I retrieving them wrong?
how i am retrieving my tags:
{% set tags = entries('tags').get() %}
<ul>
{% for tag in tags %}
{{ dd(tag) }}
{# the dd function dumps a class of EntryPresenter which contains an object of TagsTagsEntryModel not (TagsModel)#}
</ul>
Sorry for all the questions
You need to add getter functions, also. Like there:
class ProductTypeModel extends CatalogProductTypesEntryModel implements ProductTypeInterface {
protected $products; // If table do not have products column!!!
/**
* Get related products.
*
* @return ProductCollection
*/
public function getProducts()
{
return $this->products;
}
/**
* Return the products relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function products()
{
return $this->hasMany(ProductModel::class, 'type_id');
}
}
class ProductModel extends CatalogProductsEntryModel implements ProductInterface {
/**
* Get the type.
*
* @return null|ProductTypeInterface
*/
public function getType()
{
return $this->type;
}
}
@endder one quirk of generated models is that it's a good idea to bind
the generated model to the model in your addon service provider.
protected $bindings = [
TagsTagsEntryModel::class => TagModel::class,
];
This way when models are resolved YOUR model is used. Then you can put the relation methods there instead of on the generated models which should be considered hands-off.
Hope this helps!
PS @piterden outstanding advice 😊
@ryanthompson Sorry, I've been too much overclocked:/
Look,
You have create the migrations, then when you runs
Streams Platform
generates models depending on your migrations. If you set up in your migration ofreleases
(for example) and set to it field tags, which isanomaly.field_type.multiple
, then you should see thattags()
method is reveal itself inside generated modelBut there's no method
releases
insideTagsEntryModel
. If you want to add such, you have to create this method manually, but not in the model, generating by theStreams
. You'd better not edit that files anyway) But you'd create method inside your model which extends automatic generated.So what we have at the finish:
Field config in
fields
section ofmodule migration
here we sets just a listing of all fields you going to use in all streams of exactly this addon, togetheralso we need to set up each
stream migration
withassignments
andstream
config: columns in tables depends exactly on theassignments
array, but this is not straight depending, don't forget itthen, after installing module you need to open Anomaly/TagsModule/Tags/TagsModel and add to it: