Laravel Eloquent, difference between attach() and syncWithoutDetaching() - laravel

I'm wondering if there is a difference between the two following Eloquent instructions that update the data on the role_user pivot table of a many-to-many relationship between User and Role models.
// Attach the specified roles to the user
$user–>roles()–>attach([1, 2, 3]);
// Sync the specified roles without detaching the one previously assigned to the user
$user–>roles()–>syncWithoutDetaching([1, 2, 3]);
I think they do the same thing, am I correct?

"attach" method inserts the array argument without syncing or removing existing data while "syncWithoutDetaching" method updates the existing value comparing with array argument and also does not remove rest of the existing values.

Related

Eloquent model setRelation generating array instead of Collection

If you are doing $instance = $model->with('categories')->find($id); and after var_dump($instance->categories) it going to return Collection of categories.
But on the project I'm working on in some heavy queries, we are not using with and getting data with a combination of GROUP_CONCAT and CONCAT, like this:
\DB::raw('GROUP_CONCAT(DISTINCT CONCAT(categories.id, ",,", categories.name) SEPARATOR ";;") as categories'),
And then we are building relations manually parsing result and creating a relationship using $instance->setRelation($relation, $data) but for some reason, it's returning an array of objects instead of Collection.
There are also option to use setRelations() and this method returning Collection but I found if you have bidirectional relations it's creating recursion and working really slow. For example: if in User model we have set $this->hasMany('Comments') and in Comments model we have set return $this->belongsTo('User'); and after when we running setRelations() to manually build relations it is create nesting models with recursion (User->Comments->User and so on).
The third option is to not use setRelation() or setRelations() and just to manually create Collection, populating it and set to model. But in such case, it will not be set as a model relation.
Any suggestions on how to build manually in the right way (to create relation is same way eloquent creating with with).
Group return collection of collection so you have to remove the keys of first collection and for that you can use values function of collection like this
$instance->setRelation('relation', $data->values()->all());
Details https://laravel.com/docs/5.6/collections#method-values

Laravel Dynamic Eager Loading for Dynamic Relationships

Laravel Version: 5.5
PHP Version: 7+
Database Driver & Version: mysql 5.7+
Scenario:
I have a SaaS application that has flexible database structure, so its fields are bound to change, especially given it has a Json field (for any extra database structure to be created from client side of the application), including relationship based fields. so Account Table can have dynamically created employee_id field, and thus the need to access relationships dynamically
Problem:
I need to EagerLoad models based on this dynamic relationship. If I had something like this:
// Account Model
public function employee(){
return $this->belongsTo(App\Employee);
}
it would be easy. But what I have is this:
public function modelBelongsTo(){
return $this->belongsTo($dynamicClassName, $dynamicForeignKey);
}
Now if I eager load this, I'll get Account Model instance with related Employee on key modelBelongsTo. This is how Eloquent Names based on the function of eagerload. But after this I cannot use this function again to eagerload a second model because it'll just overwrite results on modelBelongsTo key.
Possible Solution Directions:
1) Can I Somehow change laravel's process to use a name I provide?
or
2) Can I write functions on the fly to overcome this so I'll write employee function on the fly?
or
3) Worst Case Scenario: I iterate over all records to rename their keys individually because I am using a pagination, it wouldn't that big of a deal to loop over 10 records.
Us a morph relationship
define the various dynamic classnames say
Employee
Boss
Morph works by having the related key and the table name stored in the parent table, it means to relate them you have to use a join or an orm and you cant have foreign key constraint on it as it links to different tables.
then have your account have morphs where
we have
Account
as top class
then we have
EmployeeAccount, BossAccount
which have their relation to boss and employee
then in Account have morphto relation call it specificAccount()
to which in its child morphs have the morph relation to Account
then add it to $with so to eager load them so when fetching account you could simply do
$account ->specificAccount
to get its morph child. which is nullable
This is totally dynamic such that if you have other classes in future you can just add and add the morph relationship. This may be applied to any reflection or runtime evaluated and loaded classes/code though it is not advisable to do this, as you can always edit code to create new functionality without affecting previous.

Laravel: Get pivot data for specific many to many relation

My User model has many Target and vice versa.
Now I've got a given User and given Target and I want to access pivot data from their relation. The pivot column is called type
How can I achieve this?
On the relationships for both User and Target, tack on a ->withPivot('type') which will instruct Laravel to include that column. Then once you have your result set, you can access the field with $user->pivot->type.
If you're not iterating over a collection, but have a user and one of their targets and want the type field, you could use $target = $user->targets->find($targetId) and access the type with $target->pivot->type.
More at http://laravel.com/docs/4.2/eloquent#working-with-pivot-tables
Laravel 8.x
After accessing the relationship, you can access the intermediate table using the pivot attribute on your models
foreach ($user->targets as $target) {
echo $target->pivot->created_at;
}
Docs: https://laravel.com/docs/8.x/eloquent-relationships#retrieving-intermediate-table-columns
You can also limit columns by passing array as 2nd arg to simplePaginate
$query->users()->simplePaginate($per_page, ['users.id', 'users.email']);

How do I add an array of pivot table data to a user?

I have a pivot table of user_activities. I already have the relationship defined in the model, for example:
$activities = User::find($id)->activities;
This returns an array of objects. I want to send the user object with the activities array.
I've tried dynamically assigning this activities array to the user object but I only get an empty object as a result ($user->activities = {}) instead of the array full of activity objects. How do I add this array of activity objects to the user object?
You may try following approach (your approach should work, related models will be loaded later on call (dynamically) but this is better, known as eager loading):
$user = User::with('activities')->find($id);
Make sure you have declared the relationship properly and have related models as well.

GAS ORM many-many relation with attributes

I'm using CodeIgniter and I'm starting to work with gas orm.
One of my m-n-relationship-tables using a composite key has also some additional attributes to the releation.
For Example:
Table teams, Table employees, and a m-n table which binds them together + adding the attribute role
Is it possible to get the attribute using GAS ORM?
Yes, it is possible.
Simply create a new relationship in one of the two tables you are going to link with the pivot table that refers to the pivot table itself as a has_many relation. (But dont do the linking stuff in the model file, eg:
ORM::has_many('\\Model\\User\\Role')
instead of
ORM::has_many('\\Model\\User\\Role => \\Model\\Role')
See http://ellislab.com/forums/viewreply/1050559/ for exact the same question.

Resources