laravel many to many relationship in this case - php

I have already checked this official example http://laravel.com/docs/eloquent#many-to-many-polymorphic-relations
but I still confused because I may have a different case.
I have a DetailsAttribute model which deals with details_attribute table.
I have a Action model witch deals with action table.
The relationship between them is many to many.
So I created a new table details_attribute_action with model DetailsAttributeAction
My DetailsAttribute model should have:
public function actions(){}
My Actions model should have:
public function detailsAttributes(){}
and my DetailsAttributeAction model should have functions but I don't know what they are.
My question is what is the code inside the previous functions please? and should really the DetailsAttributeAction have functions of not?

What you're looking for is a Many-to-Many relation, not one that is polymorphic.
http://laravel.com/docs/eloquent#many-to-many
Your code should look something like this:
class DetailsAttribute extends Eloquent {
// ...
public function actions()
{
// Replace action_id and details_attribute_id with the proper
// column names in the details_attribute_action table
return $this->belongsToMany('Action', 'details_attribute_action', 'details_attribute_id', 'action_id');
}
}
class Action extends Eloquent {
// ...
public function detailsAttributes()
{
return $this->belongsToMany('DetailsAttribute', 'details_attribute_action', 'action_id', 'details_attribute_id');
}
}
You won't have to worry about how to create the DetailsAttributeAction model in Laravel. It's simply a table to map the Many-to-Many relationships you've created.

Related

Laravel relationship with a link table - Confused

I'm playing with laravel for the first time and i'm a little confused on how to achieve a link table relationship. I have 3 tables: Tank, TankContent and Content
I have the following models:
Tank:
class Tank extends Model
{
public function getTankContent()
{
return $this->hasMany('App\TankContent', 'tankID', 'tankID');
}
}
TankContent:
class TankContent extends Model
{
public function getTank()
{
return $this->belongsTo('App\Tank', 'tankID', 'tankID');
}
public function getContent()
{
return $this->belongsTo('App\Content', 'contentID', 'contentID');
}
}
Content:
class Content extends Model
{
public function getContentTanks()
{
return $this->hasMany('App\ContentTank', 'contentID', 'contentID');
}
}
Now im trying to call say tankID 2 and get all the content details inside of that
$content = Tank::find(2);
$items = $content->getTankContent;
This will list me the content. But then how do i go about linking the results to getContent() from the TankContent model?
Thanks in advance. Hopefully I just need it explaining and then it will all click.
p.s i have tried reading the https://laravel.com/docs/5.5/eloquent-relationships#many-to-many and im still stumped!
TankContent isn't required, the belongsToMany relationship methods belong on the Tank and Content models.
https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
From here you can see there is no RoleUser model, the models are only Role and User. The belongsToMany relationship on those models defines that the intermediate, or pivot, table is role_user.
You can additionally define other fields on that relationship with withPivot() chained onto the belongsToMany method. (read "Retrieving Intermediate Table Columns"). Therefore, there is likely no reason to have a separate model for the intermediate/pivot table.

Laravel Model Extending

I have a question about models structure.
I created a model called User.php
Than I would like after get a record from DB initialize another class which extends User class based on the value from DB. I.e. there is a record from DB users
id = 1
name = John
type = 1
If type = 1 I would like to init some other class, i.g. Admin
And folders structure will be
Models
- User.php
- UserTypes
- Admin.php
How it's possible to realize this?
Thanks
You can achieve that by using for polymorphic relation. Let's say a user can be extended by two models "Admin" and "Marketer", they will look something like that:
class Admin extends Model
{
public function user() {
return $this->morphOne('App\User', 'extendable');
}
}
And the User model:
class User extends Model
{
public function extendable() {
return $this->morphTo();
}
}
Of course you will also need to add two columns to your User model extendable_id and extendable_type to hold the relation.
To read more you can check laravel documentation https://laravel.com/docs/5.5/eloquent-relationships#polymorphic-relations

Laravel Eloquent - Loading query scopes eagerly

I have a data structure in which I need objects to be aware of their needed dependencies for loading.
What I can do
Currently, I can do this to load the first layer of relationships, this is obviously a very basic model:
class Ticket {
public function notes(){}
public function events(){}
public function tags(){}
public function scopeWithAll($query)
{
$query->with('notes', 'events', 'tags');
}
}
// Loads Ticket with all 3 relationships
$ticket = Ticket::withAll();
This works great! The problem being, I need to chain this functionality down to 3-5 levels of dependent relationships. Each of the 3 loaded models is going to have n relationships of its own.
I know I can do this through eager loading if I specify all of the relationship names, as follows:
public function scopeWithAll($query)
{
$query->with('notes.attachments', 'notes.colors', 'events', 'tags', 'tags.colors.', 'tags.users.email');
}
This works great too. But I need my code to be smarter than that.
What I need to do
Statically defining the scope of each object load is not desirable at this point in my project. I need to be able to load a Ticket, and the Ticket load all of its relationships, and each of those relationships load all of their relationships.
The only way I can think to do this is find some way to eagerly load a query scope for each relationship on the class. Something like
public function scopeWithAll($query)
{
$query->with('notes.withAll()', 'events.withAll()', 'tags.withAll()');
}
Is there currently a way to do this within Eloquent?
Maybe you can try something like this:
User::withRelatives()->find(1);
Okay, that's an idea and how to implement that? For example, if you have some related methods for your User model such as 'posts', 'roles' etc then keep all the related methods (methods that make relationship) in a separate trait, for example:
trait UserRelatives {
public function posts()
{
// ...
}
public function roles()
{
// ...
}
}
Now, in the User model you may create a scopeMethod like withAll and inside there you may try something like this:
public function scopeWithAll($query)
{
// Get all the related methods defined in the trait
$relatives = get_class_methods(UserRelatives::class);
return $query->with($relatives);
}
So, if you do something like this:
$user = User::withAll()->find(1);
You'll be able to load all related models. Btw, get_class_methods(UserRelatives::class) will give you an array of all methods defined in that trait which may look something like this:
['posts', 'roles']
So, User::withAll() will load all the related models and then run the query.So, as a result the scope will do something like this:
$query->with(['posts', 'roles']);
Well, this is an abstract idea but hope you got it. Share your idea if you found something better.
Update:
According to your Model and related methods, this may look something like this:
class Ticket {
use TicketRelativesTrait;
public function scopeWithAll($query)
{
$relatives = get_class_methods(TicketRelativesTrait::class);
return $query->with($relatives);
}
}
Trait:
trait TicketRelativesTrait {
public function notes(){}
public function events(){}
public function tags(){}
}
// Loads Ticket with all relationships
$ticket = Ticket::withAll()->find(1);
This is more dynamic, no need to mention the related methods and whenever you add a new relationship method in the trait, that will also be loaded.

Using Laravel Eloquents HasManyThrough relation with multiple relations through polymorphism

I got a rather simple application where a user can report other users comments and recipes. I use a polymorphic relation to store the reports. Which works fine; however, I am now trying to get the offences that a user has made.
Getting users reports is not a problem, this can be done directly using user->reports() but I would very much like to get the reports in which other people has reported said user. I can get this to work using either the hasManyThrough relation or queries BUT only on one model at a time.
ex.
public function offenses() {
return $this->hasManyThrough('Recipe', 'Reports');
}
Or
->with('user.recipe.reports')
The problem is that my reportable object is not just recipes, it could be comments, images, etc. So instead of having to use multiple functions, the logical way would be to parse the relationship between hasManyThrough various parameters somehow.
Theoretically looking like this:
public function offenses() {
return $this->hasManyThrough(['Recipe', 'RecipeComments'], 'Reports');
}
Is this in any way possible? With some undocumented syntax? If not is there any clever workarounds/hacks?
Possible solution?
Would an acceptable solution be to add another column on my report table and only add offender_id like this?
ID | User_id | Offender_id | Reportable_type | Reportable_id
That would mean I could just make a relation on my user model that connects offences through that column. But would this be considered redundant? Since I already have the offender through the reportable model?
Models
Polymorphic Model
class Report extends Model {
public function reportable() {
return $this->morphTo();
}
public function User() {
return $this->belongsTo('App\User');
}
}
Recipe Model
class Recipe extends Model {
public function user() {
return $this->belongsTo('App\User');
}
public function reports() {
return $this->morphMany('App\Report', 'reportable');
}
}
Comment Model
class RecipeComment extends Model {
public function user() {
return $this->belongsTo('App\User');
}
public function reports() {
return $this->morphMany('App\Report', 'reportable');
}
}
Using your current model, you can receive all the reported models for a user by the following code:
$recipeCommentReport = RecipeComment::whereHas('reports',function($q){
return $q->where('user_id','=',Auth::user()->id)
});
$recipeReport = Recipe::whereHas('reports',function($q){
return $q->where('user_id','=',Auth::user()->id)
});
//Get all reports into one
$reports = $recipeReport->merge([$recipeCommentReport]);
This is messy at best, because:
We can't sort the results, given that we are using two separate db queries.
If you have other models who have a report relationship, just imagine the chaos.
The best solution, is as you have figured out above:
Add an offender_id column to your report table.
It is cleaner and follows the DRY principle.
Typical Case Scenarios
Get all Recipe Comment reports for User
Report::where('offender_id','=',Auth::check()->id)
->where('reportable_type','=','RecipeComment')
->get();
Count offense by type for User
Report::where('offender_id','=',Auth::check()->id)
->grouBy('reportable_type')
->select(Db::raw('count(*)'),'reportable_type')
->get();

Laravel: belongsTo() relation assume one-to-many relationship instead of one-to-one

I'm having a tedious problem with Laravel's ORM.
I have a model with multiple relationships, like this:
class Pages extends Eloquent {
protected $guarded = array('id','config_id');
public function template()
{
return $this->belongsTo('Templates', 'templates_id');
}
public function updateUser()
{
return $this->belongsTo('Users', 'updated_by');
}
Now I can access the template related item in a simple way, like this:
$this->template->name;
And it works out of the bat, because Laravel's ORM assumes it is a one-to-one relationship and internally calls the first() method.
But when I try the same with updateUser it fails, returning an error, saying that it can't call name on a non-object.
But if I try this:
$this->updateUser()->first()->name;
it works, but it doesn't feel right to me.
So my question is, how Laravel's ORM decide if a relationship defined with belongsTo() is one-to-one or one-to-many? Is there a way to force a needed behaviour?
Thanks
You need to define the relationship. You can define 'different' relationships on the perspective.
The ->belongsTo() function is an inverse function - but you havent defined anything on the users table - so it is wrongly assuming the inverse is one-to-many.
Just add this to your users class:
class Users extends Eloquent {
public function pages()
{
return $this->hasMany('Pages');
}
}

Categories