laravel4 How can i use eloquent in my controller? - laravel-4

I would like to create a relationship between 2 tables with eloquent but i can't find exactly how to proceed...
Here are my 2 models with relationship :
Table "etablissement":
<?php class Etablissement extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'etablissement';
public function annulation()
{
return $this->hasMany('Annulation');
}}
Table "annulation":
<?php class Annulation extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'annulation_remboursement';
public function etablissement ()
{
return $this->belongsTo('Etablissement');
}}
In the "Etablissement" table there is an id for each etablissement (id_etablissement) and in the "annulation" there is a column with the id_etablissement. How can i return in my controller a relation in order to have the etablissement's name with the annulation->id_etablissement :
class AnnulationsController extends \BaseController {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
}

It should be something like this within your index method:
$annulation = Annulation::find(1);
$annulation->etablissement->name
The annulation_remboursement table should have a establissement_id field.

Perhaps the error may be in the keys of the relation.
In https://laravel.com/docs/4.2/eloquent#one-to-one we see:
Take note that Eloquent assumes the foreign key of the relationship based on the model name. In this case, Phone model is assumed to use a user_id foreign key. If you wish to override this convention, you may pass a second argument to the hasOne method. Furthermore, you may pass a third argument to the method to specify which local column that should be used for the association:
return $this->hasOne('Phone', 'foreign_key');
return $this->hasOne('Phone', 'foreign_key', 'local_key');
[...] One To Many: Again, you may override the conventional foreign key by passing a second argument to the hasMany method. And, like the hasOne relation, the local column may also be specified:
return $this->hasMany('Comment', 'foreign_key');
return $this->hasMany('Comment', 'foreign_key', 'local_key');
* You should also checkout Defining The Inverse Of A Relation at the same page.
So, in your case you have a key named id_etablissement but Laravel is searching for etablissement_id. If you wish to override this behaviour and specify a key you should do something like:
protected $table = 'etablissement';
public function annulation()
{
return $this->hasMany('Annulation','id_etablissement');
}
and according to "The Inverse Of A Relation"
protected $table = 'annulation_remboursement';
public function etablissement ()
{
return $this->belongsTo('Etablissement','id_etablissement');
}
Note that I didn't put any of the local keys, but those will be the third parameter of the relation.

Related

Laravel belongsToMany on same table: how do I create a symmetrical relation?

I am building an admin panel using Laravel nova. I have an Items table, that has a belongsToMany to itself, using (of course) a pivot table.
This in itself is working, but when I specify that item A is related to item B, the inverse of that relation is not stored, so that results in not being found on item B.
I need it to, because the relationship is symmetrical, meaning that if A relates to B, B also relates to A.
What would be the best way to store and retrieve the relation?
This is my current code that defines the relation, and that works one way.
class Item extends Model
{
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id');
}
}
So: imagine I have a table with the items A, B and C, and the relations are defined as such in the pivot table
item_id - related_id
A - B
A - C
B - C
So basically, when I do B->related, I want it to return [A, C]. Now it will only return C, and C does not have any related items at all
There are two possible solutions here, one being to insert the inverse relation as well, resulting in
item_id - related_id
A - B
B - A
A - C
C - A
B - C
C - B
This will yield the correct results, but how to automatically force laravel to save the inverted relation as well, and making sure that deletions etc are correctly done?
The other option would be is 'merging' two related belongsToMany calls.
class Item extends Model
{
public function related(): BelongsToMany
{
// return merged $this->relatedFrom and $this->relatedTo
}
public function relatedFrom(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'related_id', 'item_id');
}
public function relatedTo(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id');
}
}
This would also cause all kind of problems.
So what would be a good approach here?
I opted for the method of adding the reverse relation to the pivot table, using an observer and a Pivot model
Normally, a model is not made when having a belongsToMany relation. In this case I needed one, so I manually created one.
The Pivot model, note it extending Pivot instead of Model, the observer being added in the boot method, and the $timestamps which I set to false:
namespace App\Models;
use App\Observers\RelationObserver;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ItemRelated extends Pivot
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'item_related';
public $timestamps = false;
public static function boot(): void
{
parent::boot();
parent::observe(new RelationObserver);
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'item_id',
'related_id',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'item_id' => 'integer',
'related_id' => 'integer',
];
}
The observer only needs a created and deleted method, inserting and deleting the reverse relation after the other is inserted or deleted respectively:
namespace App\Observers;
use App\Models\ItemRelated;
class RelationObserver
{
public function created(ItemRelated $itemRelated): void
{
if (ItemRelated::where([
['item_id', '=', $itemRelated->related_id],
['related_id', '=', $itemRelated->item_id],
])->doesntExist()) {
$itemRelatedReverse = new ItemRelated();
$itemRelatedReverse->item_id = $itemRelated->related_id;
$itemRelatedReverse->related_id = $itemRelated->item_id;
$itemRelatedReverse->save();
}
}
public function deleted(ItemRelated $itemRelated): void
{
ItemRelated::where([
['item_id', '=', $itemRelated->related_id],
['related_id', '=', $itemRelated->item_id],
])->delete();
}
}
At last I need to tell the relation on the Item table to use the pivot table, by chaining the using method:
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id')
->using(ItemRelated::class);
}

Laravel Eloquent How Can I Select Using Condition "where" for pivot table

I have three database tables called user(id,name), group(id,name) and user_group(user_id, group_id,valid_before) with relations many to many.
class User extends Model
{
protected $table = 'user';
public function groups()
{
return $this->belongsToMany(Group::class, 'user_group')
->withPivot('valid_before');
}
}
class Group extends Model
{
protected $table = 'group';
public $timestamps = false;
public function user()
{
return $this->belongsToMany(User::class, 'user_group');
}
}
How can I select all users (using Eloquent) who have
valid_before < $some_date
?
There are many ways to achieve this goal. I'll show you an example using query scopes.
In your User class you have to make a little update:
class User extends Model
{
protected $table = 'user';
public function groups()
{
return $this->belongsToMany(Group::class, 'user_group')
//->withPivot('valid_before'); <-- Remove this
}
}
and create a scope in your Group model:
class Group extends Model
{
protected $table = 'group';
public $timestamps = false;
public function user()
{
return $this->belongsToMany(User::class, 'user_group');
}
/**
* This scope gets as input the date you want to query and returns the users collection
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param string $date
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeUsersValidBefore($query, $date)
{
return $query->users()->wherePivot('valid_before', '<', $date);
}
}
Now, I imagine you have a GroupController that somewhere creates a query to retrieve the valid before users. Something like:
// [...]
$users = Group::usersValidBefore($yourDate)->get();
// [...]
If you want to create the query from the other side, I mean you want to use the User model and list all the Users that has a pivot relation with valid_before populated, than the right approach is creating a UserGroup intermediate model that can be easily used to create a query.
If you are using Laravel 8.x.x
It's much easier with Inline Relationship Existence Queries
If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the whereRelation and whereMorphRelation methods. For example, we may query for all posts that have unapproved comments:
use App\Models\Post;
$posts = Post::whereRelation('comments', 'is_approved', false)->get();
Of course, like calls to the query builder's where method, you may also specify an operator:
$posts = Post::whereRelation(
'comments', 'created_at', '>=', now()->subHour()
)->get();

Laravel Eloquent one --> many relationship assigning parent

User model has many friends primary key id
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function friends()
{
return $this->hasMany(Friend::class);
}
}
Friend model; belongs to user. Foreign key user_id.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Friend extends Model
{
protected $guarded = ['id'];
//Make sure birthday is always in right format
public function setBirthdayAttribute($value)
{
//Put in mysql format
$this->attributes['birthday'] = date("Y-m-d",strtotime($value));
}
public function user()
{
return $this->belongsTo(User::class);
}
}
Error when updating user with following controller code:
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Friend $friend
* #return \Illuminate\Http\Response
*/
public function update(Request $request, Friend $friend)
{
$friend->fill($request->all());
//Assign user to logged in user...does NOT work; things it is a column
$friend->user = \Auth::user();
$friend->save();
return redirect(action('FriendsController#index'));
}
Error message when saving:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user' in 'field list' (SQL: update `friends` set `last_name` = Muench!!, `updated_at` = 2018-09-28 13:26:01, `user` = {"id":1,"name":"Chris","email":"me#chrismuench.com","email_verified_at":null,"created_at":"2018-09-27 19:52:03","updated_at":"2018-09-27 19:52:03","braintree_id":null,"paypal_email":null,"card_brand":null,"card_last_four":null,"trial_ends_at":null} where `id` = 8)
Is there a reason I cannot assign user with a user object? I know I could set the user_id column but this would be nice if I could pass around objects.
If you want to assign object and not property in your controller you need to add the following mutator
public function setUserAttribute($user)
{
$this->attributes['user_id'] = $user->id;
}
to your Friend object.
However it could be quite risky, because relationship name is also user, so it's better to use other name (for example friend_user and then method name setFriendUserAttribute) or to use Eloquent associate method instead:
$friend->user()->associate(\Auth::user());

Clarification with eager loading/mutators/accessors for laravel

I'm making a laravel app for work and I need to load all users with their attached role without any nesting of the roles. I used this tutorial for roles: https://medium.com/#ezp127/laravel-5-4-native-user-authentication-role-authorization-3dbae4049c8a . If I use public $with = ['roles']; on my User model it returns the entire role object within the user object and I need it to just return role:'role_name';
/**
* set up eloquent between roles/user
*
* #return \Illuminate\Database\Eloquent\Relations\belongsToMany
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
Above is in my User model and below is in my Role model to define the relationships.
/**
* provides a many-to-many relationship to User model
*
* #return User::class
*/
public function users()
{
return $this->belongsToMany(User::class);
}
I thought that by adding this to the User model:
protected $appends = ['role_name'];
public function getRoleNameAttribute()
{
return $this->attribute['name'];
}
it would return everything but all it does is create a role_name: 'user_name'; On the model. So I guess I realize I'm accessing just the Users table and not the Roles table in the DB, but again not really sure what to do. Any help would be greatly appreciated.
If for the purpose of convenience you need to access the role name directly from the model you should refer to the actual relationship data:
protected $appends = ['role_name'];
public function getRoleNameAttribute()
{
return $this->roles->pluck('name');
}
This should append the array of role names to your user model. It will be an array because roles seem to have a many-to-many relationship with the User model.
The issue is you're returning $this->attributes['name'] in the getRoleNameAttribute. You want the role names, so instead you'd do something like this:
If you want an array with the names:
return $this->roles()->pluck('name')
If you want the names as a string:
return array_implode(", ", $this->roles()->pluck('name')->toArray());

Mapping of Eloquent models to tables?

We are able to create Eloquent models for tables. But how Laravel knows to which table to associate a model with? Do we have something similar to hbm.xml(mapping file we use for Hibernate) which says this model means this table.
The table name is a protected property:
class User extends Eloquent {
protected $table = 'my_users';
}
Laravel Docs
You can manually override the table name as the above answer states.
Its just a protected member of the Model.php class.
class User extends Eloquent {
protected $table = 'my_users';
}
Otherwise, a lowercase, plural format is automatically used, based on the classname of the Model. (class_basename($this))
As shown here... (Illuminate/Database/Eloquent/Model.php)
/**
* Get the table associated with the model.
*
* #return string
*/
public function getTable()
{
if (isset($this->table)) {
return $this->table;
}
return str_replace('\\', '', Str::snake(Str::plural(class_basename($this))));
}

Resources