How insert into morph related model in eloquent - laravel

Using the laravel ducmunet example :
There are there table like
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string
And 3 model (post, command and video) .
Comment model has morphTo relation with post and video.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* Get all of the owning commentable models.
*/
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
/**
* Get all of the post's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
class Video extends Model
{
/**
* Get all of the video's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
Is there a solution for insert new record into model related to the comment (video or post model).
For example if i have an instace of comment model by this instrument :
$nc = comment::find(3);
Now how can i add new post or video related to $nc comment.
I can't use save method because save method argument is an instance of post or video model but i don't know which model related to $nc comment by polymorphic.
in another word i will add a new post or video to a comment existing ($nc).

You could always use associate() and dissociate() just like BelongsTo relation. For example:
$video = Video::find(1);
$comment = new Comment();
$comment->commentable()->associate($video);
$comment->save()
Just a reminder, one comment belongs to a single video or post. Happy coding!

Improving Bagus answer with the Correct way to use associate()
$video = Video::find(1)
$comment = new Comment();
$comment->commentable()->associate($video);
$comment->save();

Related

Laravel polymorphic relation in Nova

My current blog like app has a Page that could have many Paragraphs of a different structure (Text, Image, File download, Registration form, ... ). When trying to translate this to Eloquent Models with relations, I think this is an easy way to do so:
Table pages:
id
title, ...
Table paragraphs:
id
page_id
paragraphable_id
paragraphable_type
Table paragraph_texts:
id
text
Table paragraph_images:
id
image_path
Table paragraph_downloads:
id
file_path
And the Models:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
public function paragraphs() {
return $this->hasMany(Paragraph::class);
}
}
?>
The Paragraph Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Paragraph extends Model
{
public $table = 'paragraphs';
public $timestamps = [];
public function page() {
return $this->belongsTo(Page::class);
}
public function paragraphable() {
return $this->morphTo();
}
}
The Text Type model as sample:
<?php
namespace App\Models\ParagraphTypes;
use Illuminate\Database\Eloquent\Model;
class Text extends Model
{
protected $table = 'paragraph_texts';
public function paragraph() {
return $this->morphOne(Paragraph::class, 'paragraphable');
}
}
I now want to view the page in nova and create a new paragraph by a selectable type. I created the Resource classes (for page, for paragraph and for each paragraph type) according to the eloquent relations (HasMany field for the page, MorphTo for the paragraph and just a TextArea for the Text type). When I see the details of a page and want to add a new paragraph, I can see the form to add a paragraph and can select the paragraph type in a dropdown but can only see the already existing entries, not new one. I will never want to add an existing paragraph type like this.
So questions:
Is the transformation of the structure into eloquent correct, are there any improvements?
How could laravel nova handle the creation of a new paragraph type for a page "through" the paragraph model? I feel like having to add a custom field to do so, that maybe asks for the type to create first...
Regarding your first question: You should use the relationships from the documentation:
class Page extends Model
{
public function texts() {
return $this->morphedByMany(Text::class, 'paragraphable', 'paragraphs');
}
}
class Text extends Model
{
public function pages() {
return $this->morphToMany(Page::class, 'paragraphable', 'paragraphs');
}
}
You can get multiple paragraph types with an accessor:
class Page extends Model
{
public function getParagraphsAttribute() {
return $this->texts->toBase()->merge($this->files)->merge([...]);
}
}
$paragraphs = $page->paragraphs;

Method App\Post::__toString() must not throw an exception error when I try to destroy a post

I am currently using Laravel 5.2.
When I call the destroy method on my Post controller I get the following message:
I have taken a look at this question because it seems very similar to mine, although the answers were not able to help: Exception being thrown when trying to delete model in laravel 5.2
The answers were not able to help because I am not able to find any folder called Entrust in my project, and editing the line in config/auth only gave me a different error about not being able to find "App\Models\User".
I am not really sure where else to look for this issue and would really appreciate any help or advice you can give.
Here is my destroy method and Post controller:
Post Controller
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table = 'posts';
/**
* Define relationship between posts and categories.
*
* #return eloquent relationship
*/
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
/**
* Define relationship between posts and tags.
*
* #return eloquent relationship
*/
public function tags()
{
return $this->belongsToMany('App\Tag', 'post_tag', 'post_id', 'tag_id');
}
}
Destroy Method
public function destroy($id)
{
// find the post and tags
$post = Post::find($id);
$tags = $post->tags();
// update post_tag relationship
if($tags != null)
{
$post->tags()->detach($tags);
}
// delete the post
$post->delete();
// redirect with flash data to posts.index
Session::flash('success', 'The blog post was deleted successfully!');
return redirect()->route('posts.index');
}
You are getting a query builder instance from $tags = $post->tags();, instead just do $tags = $post->tags; to get the collection.
Second, pass an array of ids to the detach method like so:
// find the post and tags
$post = Post::find($id);
$ids = $post->tags->pluck('id');
// update post_tag relationship
if(count($ids))
{
$post->tags()->detach($ids);
}
// delete the post
$post->delete();

Polymorphic Relations in Laravel naming convenstion

I am going to create polymorphic relations in Laravel but my tables are so old and it's naming conventions are not according to laravel. Can I do this and how ?
Of course you can set your table name and FK column names directly.
Look over Realtion docs and if necessary in Laravel API or source code
If you have
posts
id - integer
title - string
body - text
comments
id - integer
post_id - integer
body - text
likes
id - integer
likeable_id - integer
likeable_type - string
Then your code will be
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Like extends Model
{
/**
* Get all of the owning likeable models.
*/
public function likeable()
{
return $this->morphTo('likeable', 'likeable_type', 'likeable_id');
}
}
class Post extends Model
{
/**
* Get all of the post's likes.
*/
public function likes()
{
return $this->morphMany('App\Like', 'likeable', 'likeable_type', 'likeable_id');
}
}
class Comment extends Model
{
/**
* Get all of the comment's likes.
*/
public function likes()
{
return $this->morphMany('App\Like', 'likeable', 'likeable_type', 'likeable_id');
}
}

Laravel 4.1 eager loading 3 deep nested relationships will return undefined index on third relationship

I will be doubting thomas as much as possible. Sorry for long codes.
The problem is going to be presented using series, books, authors, authorPicture
Series Model
/**
* The Series Model
*/
namespace My\Project\Series\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Series Extends Model
{
protected $table = "series";
protected static $bookModel = "My\Project\Books\Eloquent\Book";
public function books()
{
return $this->hasMany(static::$bookModel);
}
}
Book Model
/**
* The Book Model
*/
namespace My\Projects\Books\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Book Extends Model
{
protected static $seriesModel = "My\Project\Series\Eloquent\Series";
protected static $authorModel = "My\Project\Authors\Eloquent\Author";
public function series()
{
return $this->belongsTo(static::seriesModel);
}
public function authors()
{
return $this->hasMany(static::$authorModel);
}
}
Author Model
/**
* The Author Model
*/
namespace My\Project\Authors\Eloquent;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
protected static bookModel = "My\Project\Books\Eloquent\Book";
protected static authorPictureModel = "My\Project\AuthorPictures\Eloquent\AuthorPicture";
public function authorPicture()
{
return $this->hasOne(static::$authorPictureModel);
}
public function book()
{
return $this->belongsToMany(static::$bookModel);
}
}
Author Picture Model
/**
* The Author Picture Model
*/
namespace My\Project\AuthorPictures\Eloquent;
use Illuminate\Database\Eloquent\Model;
class AuthorPicture
{
protected static $authorModel = "My\Project\Authors\Eloquent\Author";
public function author()
{
return $this->belongsTo(static::$authorModel);
}
}
Series Service
/**
* Series Service
*/
namespace My\Project\Series;
use My\Project\Series\SeriesProviderInterface;
class SeriesService
{
protected $seriesProvider;
public function __construct(SeriesProviderInterface $seriesProvider)
{
$this->seriesProvider = $seriesProvider;
}
public function findSeriesById($id)
{
$series = $this->seriesProvider->findById($id); // Will return model
return $series->with('books.authors.authorPicture')->get()->toArray();
}
}
Here comes the problem. The print_r result of SeriesFacade::findSeriesById($id) will include books and authors but not authorPicture.
The wierd thing; if I cancel return and dump DB::getQueryLog(), I can see that, a query was fired to find authorPictures in (.,.,.).
I can already associate models on save too. But I get undefined index if I try to eager load these nested relationships.
I read all the questions and even opened an issue on github laravel/laravel, but still not sure if I am doing something wrong or something else. That is why I ask this question.
I had a similar problem, which I solved. First, I submitted a query as follows (all the relationships had been set up correctly)
$batch = Batch::with('batchItem.teamMember.employee.employeeType')->find($id);
Then I got one of the batch items
$batchItem = $batch->BatchItem[0];
But when I print_r($batchItem) the last relation employeeType did not show up. Even more disconcerting, when I attempted to get data from $batchItem, for instance
$batchItem->teamMember->employee->name
It would issue more queries, as if no data was eager loaded at all!
The solution was sublime. It appears Eloquent is extremely case sensitive...
$batchItem = $batch->batchItem[0];
solved all of the issues!
Not sure if this is your problem, but perhaps it might help.
The query was being executed and the data was not being presented because;
the authorPicture relation was not set under $visible array of authors. That was not a bug. That was a feature I couldn't mastermind.

Laravel 4.1: proper way to retrieve all morphedBy relations?

Just migrated to 4.1 to take advantage of this powerful feature.
everything seems to work correctly when retrieving individual 'morphedByXxxx' relations, however when trying to retrieve all models that a particular tag belongs to -- i get an error or no results.
$tag = Tag::find(45); //Tag model name = 'awesome'
//returns an Illuminate\Database\Eloquent\Collection of zero length
$tag->taggable;
//returns Illuminate\Database\Eloquent\Relations\MorphToMany Builder class
$tag->taggable();
//returns a populated Collection of Video models
$tag->videos()->get();
//returns a populated Collection of Post models
$tag->posts()->get();
My Tag Model class loooks like this:
class Tag extends Eloquent
{
protected $table = 'tags';
public $timestamps = true;
public function taggable()
{
//none of these seem to function as expected,
//both return an instance of MorphToMany
//return $this->morphedByMany('Tag', 'taggable');
return $this->morphToMany('Tag', 'taggable');
//this throws an error about missing argument 1
//return $this->morphToMany();
}
public function posts()
{
return $this->morphedByMany('Post', 'taggable');
}
public function videos()
{
return $this->morphedByMany('Video', 'taggable');
}
}
And the Post and Video models look like this:
class Post extends Eloquent
{
protected $table = 'posts';
public $timestamps = true;
public function tags()
{
return $this->morphToMany('Tag', 'taggable');
}
}
I am able to add/remove Tags to Posts and Videos as well as retrieve the related Posts, and Videos for any Tag -- however -- what is the proper way to retrieve all Models having the Tag name 'awesome'?
Was able to figure it out, would love to hear comments on this implementation.
in Tag.php
public function taggable()
{
return $this->morphToMany('Tag', 'taggable', 'taggables', 'tag_id')->orWhereRaw('taggables.taggable_type IS NOT NULL');
}
in calling code:
$allItemsHavingThisTag = $tag->taggable()
->with('videos')
->with('posts')
->get();
I just used this on Laravel 5.2 (not sure if it is a good strategy though):
Tag model:
public function related()
{
return $this->hasMany(Taggable::class, 'tag_id');
}
Taggable model:
public function model()
{
return $this->belongsTo( $this->taggable_type, 'taggable_id');
}
To retrieve all the inverse relations (all the entities attached to the requested tag):
#foreach ($tag->related as $related)
{{ $related->model }}
#endforeach
... sadly this technique doesn't offer eager load functionalities and feels like a hack. At least it makes it simple to check the related model class and show the desired model attributes without much fear to look for the right attributes on the right model.
I posted a similar question in this other thread as I am looking for relations not known in advance.

Resources