Laravel relationship with a link table - Confused - php

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.

Related

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

One-To-Many Relationships in laravel eloquent

Good morning, I am having a little trouble with model relationships in Eloquent, I need to link articles and images for those articles with an intermediate table. In the intermediate table I'd like to add the id's of both article and image, and I would like to retrieve all the images belonging to an article, what would be the best way to manage the relationship? Thanks in advance
You don't need to use pivot table since it's one-to-many relationship.
Just use hasMany() relation:
public function images()
{
return $this->hasMany('App\Image');
}
And then use eager loading to load all images with article:
$article = Article::with('images')->where('id', $articleId)->first();
You can use morphMany() relationship (Polymorphic Relationship) to solve your problem like this:
UPDATE: The table structure goes like this:
- articles
- id
- title
- content
- ...
- images
- id
- owner_id
- owner_type (Here there can be - Article, Auction, User, etc)
- name
- mime_type
- ...
Polymorphic relations allow a model to belong to more than one
other model on a single association. For example, imagine users of
your application can "comment" both posts and videos. Using
polymorphic relationships, you can use a single comments table for
both of these scenarios.
You models will look like this:
class Article extends Model
{
public function images()
{
return $this->morphMany(Image::class, 'owner');
}
}
class Image extends Model
{
public function owner()
{
return $this->morphTo();
}
}
To save multiple images to an article, you can do like:
$article->images()->create([... inputs_arr ...]);
and to fetch them, you can do this like:
$articleImages = Article::find($id)->images;
Hope this helps!
In Image model class
class Image extends Model
{
public function article()
{
return $this->belongsTo(Article::class);
}
}
Then you can access all the images that belong to Article as follows.
$image= Image::first();
Then for example when we want to get the name of the image that belongs to Article.
$imageName = $image->article->name;

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 many to many relationship in this case

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.

Laravel - Selecting specific columns on many-to-many relationships

I have two models in my Laravel 4.2 web application, User and Group. A user can be a member of many groups, and a group can have many members. Both models are thus joined with a many-to-many relationship:
<?php
class User extends Eloquent {
public function groups()
{
return $this->belongsToMany('Group');
}
}
class Group extends Eloquent {
public function users()
{
return $this->belongsToMany('User');
}
}
?>
One of my API resources is /groups, which lists all groups available within the app:
<?php
$groups = Group::with('users')->all();
?>
This works, however in the JSON response each user contains all fields from the users table (excluding of course those in the $hidden attribute). I would like this relationship to return only a specific set of fields instead of the whole table.
In other relationship types I can easily achieve this with the following statement (assume now that users may belong to only one group):
<?php
public function users()
{
return $this->hasMany('User')->select(['id', 'first_name', 'last_name']);
}
?>
However the above does not seem to work with many-to-many relationships. I came across this question which apparently refers to the same issue and it looks like this was not possible in Laravel 4.1. The author of the chosen answer, tptcat, provides a link to an issue on Laravel's Github issue tracker, but the link is no longer working and I couldn't figure whether this issue is still open in 4.2.
Has anybody come across this and successfully managed to solve it?
{
return $this->belongsToMany('User')->select(array('id', 'name'));
}
use this
The all method takes in an array of column names as a parameter.
If you look at the source, it takes * (which means everything) by default.
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L624-L629
You can pass in the columns that you needed and it should return the results only with the specified columns.
<?php
$groups = Group::with('users')->all(array('first_column', 'third_column'));
Use like this.
<?php
class User extends Eloquent {
public function groups()
{
return $this->belongsToMany('Group')->select(array('id', 'name'));
}
}
class Group extends Eloquent {
public function users()
{
return $this->belongsToMany('User')->select(array('id', 'name'));
}
}
?>
Instead of selecting column in relationship, you can select column as below:
$groups = Group::with('users:id,first_name,last_name')->all();
And when you are selecting column in relationship, make sure that you are selected foreign key of relation table

Categories