I'm trying to create a tagging system and now I got two table as the picture below
tags
taggables
I would like to tag the Journal and id based on the `tag_id. However, the journal always create a new record in the taggables table.
Here is my model relationship
Tag
class Tag extends Model
{
public function purchases()
{
return $this
->morphedByMany('Purchase', 'taggable');
}
public function taggables()
{
return $this->hasMany('Taggable');
}
}
Purchase
class Purchase extends Model
{
public function tags()
{
return $this
->morphToMany('Tag', 'taggable');
}
}
Journal
class Journal extends Model
{
public function tags()
{
return $this
->morphToMany('Tag', 'taggable');
}
}
If I understood correctly you are trying to use tags for journals and purchases at the same time.
Then your table structure should be something like:
journals(id, ...)
purchases(id, ...)
tags(id, name)
taggables(id, tag_id, taggable_id, taggable_type)
Then the models that can be tagged should both have a method to retrive their tags:
app/Journal.php
// namespace and use statements
class Journal extends Model
{
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
app/Purchase.php
// namespace and use statements
class Purchase extends Model
{
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
The your tag model that can be applied to both Purchase and Journal should have N methods, where N is the number of different models you are connecting through your polymorphic relation, in this example N=2 (one method to retrive purchases from a tag, and one method to retrive journals from a tag).
app/Tag.php
// namespace and use statements
class Tag extends Model
{
public function journals()
{
return $this->morphedByMany('App\Journals', 'taggable');
}
public function purchases()
{
return $this->morphedByMany('App\Purchase', 'taggable');
}
}
After the relations are set you can retrive the tags assigned to a journal or a purchase:
$journal = \App\Journal::first();
foreach ($journal->tags as $tag) {
// ...
}
or retrive all the journals or purchases linked to a specific tag:
$tag = \App\Tag::first();
// $tag->journals will contain the journals model instances linked to the current tag
// $tag->purchases will contain the purchases model instances linked to the current tag
Note: to define relationships use the FQDN of the related class, for instance: 'App\Tag' instead of 'Tag', otherwise the class would not be found by the autoloader.
Related
What is the correct way of defining 1:n relations in Laravel using MongoDB between two models Author and Book where one author can have several books while one book has exactly one authors?
Author.php
class Author extends Model
{
public function books()
{
// what to place here?
return $this->...
}
}
Book.php
class Book extends Model
{
public function author()
{
// what to place here?
return $this->...
}
}
Controller.php
class BookController extends Controller
{
public function store (Author $author, Request $request)
{
$book1 Book::create();
$book2 Book::create();
// connect $book1 <-> $author and $book2 <-> $author
// what to place here?
$book1->save();
$book2->save();
}
}
After calling store() I want to be able to do the following request
dump ($book1->author); // return $author
dump ($author->books); // return Collection of $book1 and $book2
assume you have the foreign key author_id in books table
class Author extends Model
{
public function books()
{
return $this->hasMany(Book::class, 'author_id');
}
}
class Book extends Model
{
public function author()
{
return $this->belongsTo(Author::class, 'author_id');
}
}
class BookController extends Controller
{
public function store (Author $author, Request $request)
{
$book1 = new Book();
$book2 = new Book();
$author->books()->saveMany([$book1, $book2]);
}
}
Suppose you have the ff:
users = model a
posts = model b
Case 1: vice versa of Case 2:
Case 2:
// on posts table/document you should have a user_id (as foreign key)
// and on post model you need define the belongsTo relationship
$this->belongsTo(User::class)
// on the User model you have to define hasOne or hasMany
$this->hasOne(Post::class) or $this->hasMany(Post::class)
Case 3:
On case 3, I'm not sure if adding a reference to each other is necessary because creating a single reference that links them can provide the data you require regardless of whether the request comes from model a or model b.
It is possible that it will not solve your problem. However, hopefully it will be of some assistance to you.
For case 1 ( in model_a ):
public function model_b_many()
{
return $this->hasMany( Model_b::class, 'id' );
}
For case 2 ( in model_b ):
public function model_a_many()
{
return $this->hasMany( Model_a::class, 'id' );
}
This will also work for case 3 :)
I have the following route:
Route::get('/api/products/{product}', 'ProductController#get');
My Product model looks like this:
class Product extends Model
{
public function ingredients()
{
return $this->belongsToMany(Ingredient::class)->withPivot('value');
}
}
In my controller, the method is:
public function get(Product $product)
{
return $product;
}
This only returns the attributes of the Product object as a JSON. I would also like to return the related ingredients and pivot table values (as it would with the with method), and possibly other related models.
return $product->with('ingredients') creates a collection of all Products, so that doesn't really work, I have to filter it again by the product ID. I can obviously construct the JSON myself, but that becomes tedious if I want multiple related models included. Is there an easy way to accomplish this?
You have three options:
Using $with in model
class Product extends Model
{
protected $with = ['ingredients'];
public function ingredients()
{
return $this->belongsToMany(Ingredient::class)->withPivot('value');
}
}
Load the relation and return product:
public function get(Product $product)
{
$product->ingredients;
return $product;
}
Use the load method on the product:
public function get(Product $product)
{
return $product->load('ingredients');
}
I am trying to store statuses of multiple models in a polymorphic relationship and use them from there.
The retrieving of the status works but besides that I need to get all (available) the statuses belonging to that specific model. And then eager load them so I can use them in a select form or somewhere else.
Here is a representation:
// Status.php
class Status extends Model
{
public function statusable()
{
return $this->morphTo();
}
}
// Article.php
class Article extends Model
{
public function status()
{
return $this->morphOne(Status::class, 'statusable', 'statusable_type', 'statusable_id', 'status');
}
}
// Post.php
class Post extends Model
{
public function status()
{
return $this->morphOne(Status::class, 'statusable', 'statusable_type', 'statusable_id', 'status');
}
}
Here I can get the status(details) of the selected model like App\Article::first()->status()->get() or eager load it as expected. What I want is to add a method where I can call (all) statuses that belong to that particular model. For example:
$article = App\Article::first()->statuses();
and then return all available statuses to this model. I can (did) of course create a method like:
// Article.php
...
public function statuses()
{
$statuses = Status::where('statusable_type', self::class)->get();
return $statuses;
}
But this way I cant eager load it because I don't return a relationship. So is there a cleaner way where I can use eager loading as well?
I have articles that can contain different tags. For example, 5 pieces: (php, html, css, laravel, js) And I have groups that can also contain different tags. For example 4 pieces: (laravel, php, html, css)
I have already defined the relationships and it works. What I still lack is the linkage of articel to the group.
How can I show all posts in the group where the tags match?
Does anyone have an idea or can help me?
Post Model
public function groups()
{
return $this->belongsToMany('App\Group');
}
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
Tag Model
public function articles()
{
return $this->morphedByMany('App\Article', 'taggable');
}
public function groups()
{
return $this->morphedByMany('App\Group', 'taggable');
}
Group Model
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
public function articles()
{
return $this->belongsToMany('App\Article');
}
Since your question is updated I have seen a mistake in relationships
change article_group table name to taggables. Because laravel is doing some task on the basis of other table names. If you still want to use article_group then you will have to explicitly tell laravel about table names.
Read HasRelationships.php (full path is given below) trait then you will have idea about how laravel is linking them automatically.
/your_project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php
Yes
Solution to your requirement is Many To Many Polymorphic Relations
Read it here https://laravel.com/docs/5.6/eloquent-relationships#many-to-many-polymorphic-relations.
You will create 4 tables as shown below and then create relationships in Models
articles
id - integer
name - string
groups
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
taggable_id - integer
taggable_type - string
Relationship will be like this:
class Article extends Model
{
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
Inverse Relatioship will be like this:
class Tag extends Model
{
public function articles()
{
return $this->morphedByMany('App\Article', 'taggable');
}
public function groups()
{
return $this->morphedByMany('App\Group', 'taggable');
}
}
Now you can get tags of Article like this:
$article = App\Article::find(1);
foreach ($article->tags as $tag) {
//
}
Attaching Articles To The Groups is As Follows:
$article = App\Article::find(1);
$group = App\Group::find(1);
$article->groups()->sync($group->id);
Read this for more https://laravel.com/docs/5.6/eloquent-relationships#updating-many-to-many-relationships
Hope this solves your problem.
I have three models that relate to each other one to many:
Country
class Country extends Model
{
protected $fillable=['name','sort'];
public $timestamps=false;
public function region(){
return $this->hasMany('App\Models\Region');
}
}
Region
class Region extends Model
{
protected $fillable=['country_id','name','sort'];
public $timestamps=false;
public function country()
{
return $this->belongsTo('App\Models\Country');
}
public function city()
{
return $this->hasMany('App\Models\City');
}
}
City
class City extends Model
{
protected $table='cities';
protected $fillable=['region_id','name','sort'];
public $timestamps=false;
public function region()
{
return $this->belongsTo('App\Models\Region');
}
}
When we remove the country automatically, remove all child item relationship, that is, removed and regions and city this country
I am doing so:
Model Country
public static function boot() {
parent::boot();
static::deleting(function($country) {
//remove related rows region and city
// need an alternative variation of this code
$country->region()->city()->delete();//not working
$country->region()->delete();
return true;
});
}
}
OR
Model Region
public static function boot() {
parent::boot();
// this event do not working, when delete a parent(country)
static::deleting(function($region) {
dd($region);
//remove related rows city
$region->city()->delete();
return true;
});
}
}
options with cascading deletes database, please do not offer
UPDATE
I found the answer
use closure for query builder, to remove related models
Model Country
public static function boot() {
parent::boot();
static::deleting(function($country) {
//remove related rows region and city
$country->region->each(function($region) {
$region->city()->delete();
});
$country->region()->delete();//
return true;
});
}
Laravel Eloquent ORM - Removing rows and all the inner relationships
Just a quick recap:
$model->related_model will return the related model.
$model->related_model() will return the relation object.
You can do either $model->related_model->delete() or $model->related_model()->get()->delete() to access the delete() method on the model.
Another way of handling the deletion of related (or sub) models is to use foreign key constraints when you write your migrations, check https://laravel.com/docs/master/migrations#foreign-key-constraints
I think you can do this in the delete function of the parent object:
public function destroy_parent($id)
{
$parent = PARENT::find($id);
foreach ($parent->childs as $child){
$child->delete();
}
$parent->delete();
return redirect(...);
}