Polymorphic Relationships and same name model and morphTo function - php

So I am trying to create a taggable table which enables multiple models to have tags associated with them and felt that Laravel's Polymorphic relationships would be the way to go.
Unfortunately I can't seem to get them working in the following setup. As I receive the following error when running php artisan migrate:refresh --seed.
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class name must be a valid object or a string","file":"...\\vendor\\laravel\\framework\\src\\
Illuminate\\Database\\Eloquent\\Model.php","line":527}}
I believe the issue is due to the fact that the Taggable model has the same morphTo named as outlined below. Since changing this fixes the issue. Why does this cause a problem?
Taggable model
class Taggable extends Eloquent {
protected $table = 'taggable';
public function taggable()
{
return $this->morphTo();
}
}
Track model
class Track extends Eloquent {
protected $table = 'tracks';
protected $fillable = array('title', 'year', 'image');
protected $guarded = array('id');
public function playlists()
{
return $this->belongsToMany('Playlist');
}
public function tags()
{
return $this->morphMany('Taggable', 'taggable');
}
}
Tag model
class Tag extends Eloquent {
protected $table = 'tags';
protected $fillable = array('title', 'description');
protected $guarded = array('id');
}
Migration
Schema::create('taggable', function(Blueprint $table)
{
$table->increments('id');
$table->integer('taggable_id');
$table->string('taggable_type');
$table->integer('tag_id');
$table->timestamps();
});
DatabaseSeeder Snippit
...
DB::table('taggable')->delete();
$track1 = Track::find(1);
$idm = Tag::find(1);
$track1->tags()->create(array('tag_id' => $idm->id));
...
Any help would be much appreciated on this matter.

Try simplifying your relationships - you appear to be using an intermediate table to allow for tag reuse - which is commendable but is complicating your issue.
Start with the Tag and Track models - add additional complexity after you have the basic polymorphic relationship working.
Also, the issue might be that you are using the same name for your Model as the actual relationship function is named. IE Taggable->taggable() vs Tag->taggable()

Related

Why cant I assign $with in the model of both end of the relationship

So I have 2 models in this example that is Gig and GigImage. Here's the Gig model
class Gig extends Model
{
use HasFactory, Uuids, SoftDeletes;
protected $fillable = [...]
protected $with = ['gigImages'];
public function gigImages()
{
return $this->hasMany(GigImage::class);
}
}
as you can see $with is referencing the GigImage and so far things worked as expected.
The problem comes when i did the same thing for GigImage
class GigImage extends Model
{
use HasFactory;
protected $fillable = ['image', 'gig_id', 'is_thumbnail'];
protected $with = ['gig'];
public function gig()
{
return $this->belongsTo(Gig::class);
}
}
After I assign the GigImage instance to Gig and tried to execute Gig::first() on tinker it just freezes for a minute and stops working entirely by itself, there's no error message. This occurs when I tried to mention anything related to those two models with any eloquent method. Maybe there's some sort of endless loop going on but I'm not sure what's wrong.

Eloquent many-to-many returning an empty value

Good day. I am trying to convert some of my models to use Eloquent mechanisms from table joins. I have three tables, and I want to use one of them as a pivot table. The three tables are shown below
First table (cp_cases_counsel)
Followed by (cp_counsel)
Then,(cases_sc)
I am trying to implement a many-to-many relationship between cases_sc and cp_counsel using cp_cases_counsel as the pivot table.
These are my models
use App\Models\SupremeCases;
use Illuminate\Database\Eloquent\Model;
use App\Models\CounselCase;
class Counsel extends Model
{
//
protected $fillable = [];
protected $table = 'cp_counsel';
protected $connection = 'mysql2';
public function supreme()
{
return $this->belongsToMany(SupremeCases::class,'cp_cases_counsel','counsel_id','suitno');
}
}
For the Cases Model
use Illuminate\Database\Eloquent\Model;
use App\Models\Counsel;
class SupremeCases extends Model
{
//
protected $connection = "mysql2";
protected $fillable = [];
protected $table = 'cases_sc';
public $timestamps = false;
public function counsels()
{
return $this->belongsToMany(Counsel::class,'cp_cases_counsel','counsel_id','suitno');
}
}
Getting the counsels and their cases using
$counsels = Counsel::with('supreme')->get();
I get an empty result for the supreme node in the response as shown below
Please, what I'm I doing wrong?

Laravel connection attribute ignored with hasmanythrough

Summary
I am trying to use hasManyThrough relationship with tables from different connections.
Description of the problem:
When defining a hasManyThrough relationship, the protected $connection attribute is ignored.
Steps To Reproduce:
Create the following models (Abbreviated code)
class Resource extends Model
{
protected $connection = 'tcollect'
public function absences()
{
return $this->hasManyThrough('ARM\TargetHoraire\Absence', 'ARM\Tcollect\ICO\ICOExternalReference', 'RecordID', 'ResourceID', 'ID', 'ExternalKey');
}
}
class Absence extends Model
{
protected $table = 'Absences';
protected $connection = 'punch';
}
class ICOExternalReference extends Model
{
protected $table = 'ICOExternalReferences';
protected $connection = 'tcollect';
}
Then call the relationship on the resource model
$resource->absences;
Notice that ICOExternalReference $connection attribute is ignored. It tries to use the ICOExternalReference from the punch connection.
Question
Is there any problem with my code or is there aworkaround to solve this problem?
I have created an issue on the framework repo, but it got closed right away.
Try with
hasMany(class with all parameter)

laravel 5.0 save data in pivot table that is used as a model

I am building a custom templating system for my newsletters where you can add pre defined Blocks to a newsletter. These will be stored as serialized html in my Blocks table.
So a newsletter can have many blocks and a block can have many newsletters so we got a many-to-many relation here. (One block also has a certain amount of BlockItems)
Now I am trying to use the newsletters_blocks pivot table as a seperate model to put an other relation on this pivot table. So I can store the content in that new table. This way the blocks and blocks_items table are pre defined and will stay untouched and it will be easy to add new ones in the future.
My database looks like this:
My models look like this:
Newsletter Model:
class Newsletter extends Model
{
public function blocks()
{
return $this->belongsToMany('Block', 'blocks', 'newsletter_id', 'blocks_id')
->withPivot(
'block_order',
'block_item_content_id'
)
->as('block_item_content')
->using(BlockNewsletterPivot::class);
}
}
Block Model:
class Block extends Model
{
protected $table = 'blocks';
protected $fillable = ['name', 'image_thumbnail', 'image', 'html'];
public $timestamps = false;
public function newsletter()
{
return $this->hasMany('App\Newsletter');
}
public function blockItems()
{
return $this->hasMany('App\BlockItem');
}
}
BlockItem Model:
class BlockItem extends Model
{
protected $table = 'blocks_items';
protected $fillable = ['block_id', 'item_type', 'html_key'];
public $timestamps = false;
public function block()
{
return $this->belongsTo('App\Block');
}
}
The pivot table newsletters_blocks Model
class NewsletterBlockPivot extends Pivot
{
public function blockItemContent()
{
return $this->belongsTo(BlockItemContent::class);
}
}
BlockItemContent Model:
class BlockItemContent extends Model
{
protected $table = 'block_item_content';
protected $fillable = ['item_type', 'html_key', 'content', 'properties'];
public $timestamps = false;
}
Now if I want to access the content field of the block_item_content table I can do this like so:
$newsletter->newsletters_blocks->blockItemContent->content
But now I am trying to link a block to a newsletter but I am not sure how I can do this. To be honest I am not even sure if it's possible what I am trying to do here.
So, you are trying to access:
$newsletter->newsletters_blocks->blockItemContent->content
Let's analyse this:
First part
$newsletter->newsletters_blocks
In your Newsletter Model you have defined the many to many relationship with blocks table using the function blocks() inside the function:
$this->belongsToMany('Block', 'blocks', 'newsletter_id', 'blocks_id') should be something like:
$this->belongsToMany(Block::class, ...everything else should be fine if correct...)
So, you can access many blocks of one newsletter as below:
$newsletter->blocks
but not $newsletter->newsletters_blocks
Now, Second part
let's go forward: $newsletter->blocks->blockItemContent
What does it mean? if $newsletter->blocks means many blocks, then how can we access blockItemContent of many blocks all at once?
you have to do something like:
foreach($newsletter->blocks as $block){
dump($block->blockItemContent->content);//here it is
}
This is how it should work if all your define relationships are correct and all the database columns and foreign keys are correct. Best of luck.

Fire Laravel events on touch

this is my scenario:
I'm using Laravel 5.5.x.
I have two models, linked in one to many way.
class Artwork extends Model
{
//It has timestamps
protected $table = 'artworks';
protected $fillable = [
'artwork_category_id'
];
public function artworkCategory()
{
return $this->belongsTo('App\Models\ArtworkCategory');
}
}
class ArtworkCategory extends Model
{
use SoftDeletes;
protected $touches = ["artworks"];
/**
* #var string
*/
protected $table = 'artwork_categories';
protected $fillable = [
'category_name',
'acronym',
'deleted_at'
];
public function artworks()
{
return $this->hasMany('App\Models\Artwork');
}
}
Touch works correctly, so when I update an artwork category the related artworks updated_at field is updated.
But I need to listen the "touch" event on each artwork.
I've tried inserting "updated" listener on boot method in AppServiceProvider, but it is not fired.
Artwork::updated(function ($model){
\Log::debug("HERE I AM");
});
I've tried using an observer, but no luck.
class ArtworkObserver
{
public function updated(Artwork $artwork)
{
dd($artwork);
}
}
Boot method in AppServiceProvider:
Artwork::observe(ArtworkObserver::class)
Question:
Could somebody show me the right way to do it? Or tell me where am I wrong?
I was not good enough to find an example that helps me how to do it.
Update
I need to achieve this because I have to "fire" Scout to save updated data on Elasticsearch on Artwork index.
Most probably $touches uses mass update, and if you check Events section of Eloquent you'll find following:
When issuing a mass update via Eloquent, the saved and updated model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
The best that I can think of is that, you update Artworks manually (instead of $touches) when a ArtworkCategory is updated:
class ArtworkCategory extends Model
{
use SoftDeletes;
protected $fillable = [
'category_name',
'acronym',
'deleted_at'
];
public function artworks()
{
return $this->hasMany('App\Models\Artwork');
}
public static function boot()
{
parent::boot();
static::updated(function($artworkCategory)
{
$artworkCategory->artworks->each(function($artwork) {
$artwork->setUpdatedAt($artwork->freshTimestamp());
$artwork->save(); /// Will trigger updated on the artwork model
});
});
}
}

Categories