What are the differences between viaTable() and via()? - php

I can't understand the difference between viaTable() and via(). Tried to search the information on the internet, but couldn't find any useful ones. Could someone explain me that? When which one should be used?

via() uses exisiting relation name so you have to create the method establishing the relation first.
viaTable() allows to connect another table "on-the-fly" so you don't have to use exisiting relation name (so you don't have to create method establishing the relation) but you need to configure it using this method's arguments.

When you are defining Many_To_Many relations you can use both, the difference is when you use via(), you need to define a relation before that (usually in your junction table's model) and use that relation with via() to define it as a Many_To_Many relation which points to the junction table's model. But when you use viaTable(), you can define Many_To_Many relations only with using the junction table's name and only between your two main table models (no need to define relation in your junction table's model).
Here is a brief explanation from Yii2 guide:
When declaring such relations, you would call either via() or
viaTable() to specify the junction table. The difference between via()
and viaTable() is that the former specifies the junction table in
terms of an existing relation name while the latter directly uses the
junction table.
Here are two examples of defining Many_To_Many relations using both approaches from Yii2 documentation (Its a Many_To_Many relation for an online market which an order can have multiple items[things that are being sold in this market] and also an item can be for multiple orders of different people or the same person):
Defining relation using via():
class Order extends ActiveRecord
{
public function getOrderItems()
{
return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
}
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->via('orderItems');
}
}
Defining relation using viaTable():
class Order extends ActiveRecord
{
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->viaTable('order_item', ['order_id' => 'id']);
}
}
P.S: I personally think using viaTable() is more logical and convenient.
P.S: You can find complete and well-explained documentation about Many_To_Many relations in Yii2 from This Section of its document.

As in Yii2 Guide ...
When declaring such relations, you would call either via() or
viaTable() to specify the junction table.
The difference between via()
and viaTable() is that the former specifies the junction table in
terms of an existing relation name while the latter directly uses the
junction table. For example,
http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#junction-table

Related

Laravel model relation - change to mutator, without chaning anything in cotrollers

I have 2 tables, estimates and models.
There's a 1-to-1 relation between the two.
I want to move model names from the models table to the estimates table. That will be done with a script that I will have to write myself.
The way this database was set up was wrong, for many reasons I don't need to specify here.
The models table has only 2 columns - id and name.
I access this relation in a lot of controllers, and views all over my app :
$estimate->model_info->name
So I would keep both the models table (with no records) and the Model.php model, keep the old code in the controllers and views, but the new code of accessing model names would be just :
$estimate->name
In the Estimate.php model I have this relation :
public function model_info() {
return $this->hasOne('App\Models\Model', 'id', 'model_id');
}
How can I just change this relation into a mutator, so the old way of accessing model names and the new way would work at the same time?
I've tried the withDefault() callback method with no luck, it returns an empty value :
public function model_info() {
return $this->hasOne('App\Models\Model', 'id', 'model_id')
->withDefault([
'name' => $this->attribute->name
]);
}
Do I have to update my code in all controllers and views or if there's an easier way to do this?

Lumen - Eloquent: Override name of joining table

I'm building a DB for a software where authentication is coupled with the companys LDAP Server.
I now have the two tables
AD_Groups
and
AD_Users
Which are joined in the table
AD_UsersXAD_Groups
I already learnt about establishing relationships in eloquent.
The many to many relationship is exemplified in the official docs by this:
https://laravel.com/docs/5.8/eloquent-relationships#many-to-many
Now, as you can see, the following feature of eloquent won't help me much:
"To define this relationship, three database tables are needed: users, roles, and role_user. The role_user table is derived from the alphabetical order of the related model names, and contains the user_id and role_id columns."
I therefore need to override this derived name by using the second parameter, as described here:
"As mentioned previously, to determine the table name of the relationship's joining table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the belongsToMany method:
return $this->belongsToMany('App\Role', 'role_user');
But as seen in the above example from the docs, the infamous "snake case" is still applied to the name.
However, I'm affraid this might not work for my case.
Admittedly, AD_UsersXAD_Groups is pretty ugly, and I fear that eloquent/lumen will not be able to correctly identify its elements and apply the snake case rule correctly.
But I don't know for sure, and therefore I'm asking you what will be the most likely to work.
Using AD_UsersXAD_Groups or AD_UserXAD_Group
Because you have an "x", the Eloquent magic will never be able to match your table automatically.
You can override the table name in the relationship in your User model. You can also specify the keys if they are not Eloquent's expected "group_id" and "user_id":
function groups() {
return $this->belongsToMany(GroupModel::class, 'AD_UsersXAD_Groups', 'user_id_key', 'group_id_key')
}
And in your Group model you could do this to reverse it
function users() {
return $this->belongsToMany(UserModel::class, 'AD_UsersXAD_Groups', 'group_id_key', 'user_id_key')
}

L5.5 relationship of pivot table with another model

In my application, a model Device has a many-to-many relationship with model Task.
A combination of Device and Task could be assigned to a various number of model User.
example: Device A has a Task Check something and this should be done by User Frank and Steven.
From my point of view, this should be a "standard problem", but so far I could not find a proper solution.
Right now, I use following workaround:
a) added an unique ID id to the device_task pivot table
b) query id from the pivot table
c) create a new table device_task_user which contains user_id and device_task_id
b) use query builder to get/add users
But I am really not happy with this approche.
Would it be possible, that the pivot table also extends Model and then have a one-to-many relationship with User?
Or would you suggest to add a json colum to the pivot table and store the users there?
Any idea would be very welcome!
Would it be possible, that the pivot table also extends Model
Yes, it's possible. From the docs:
If you would like to define a custom model to represent the intermediate table of your relationship, you may call the using method when defining the relationship. All custom models used to represent intermediate tables of relationships must extend the Illuminate\Database\Eloquent\Relations\Pivot class
You also can create a new hasMany() and belongsTo() relationships between Task and Device models and use them as well as existing belongsToMany relationship. And you'll need to define a new relationship between pivot model and User model to be able to get data by device, task or user.
Modify many-to-many relationship to hold an extra field user_id
class Device extends Model
{
public function tasks()
{
return $this->belongsToMany(
Task::class,
'device_task',
'device_id',
'task_id'
)->withPivot('user_id');
}
}
And when updating do like this in controller
$device->tasks()->attach([$taskId]=>['user_id']=>$userId);
And of-course you need DeviceTask model and also a has-many relationship between User model and DeviceTask model to get user's task

Should some relationship tables have their own models?

I'm writing an API with an MVC framework in PHP, and I'm using the Eloquent ORM.
My app has some models which are eventually linked through relationship tables, but intended to be created in a separate, decentralized manner.
Should these relationship tables have their own models, or should the models that are related have methods to create links?
With Eloquent, in regards to intermediate or pivot tables with many to many relationships, you shouldn't need to create an additional model.
You should always set up the relationships for related Models with the belongsToMany() method, documented here: https://laravel.com/docs/5.3/eloquent-relationships#many-to-many
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
They have various methods of then using this relationship to adding or updating items to the pivot table including attach, detach, or sync documented here: https://laravel.com/docs/5.3/eloquent-relationships#updating-many-to-many-relationships
$user = App\User::find(1);
$user->roles()->attach($roleId);
You can also add data to extra fields:
$user->roles()->attach($roleId, ['expires' => $expires]);
Extra fields on the pivot table are important when you have data, like timestamps, that relate to the relationship and not either of the related models.
An example I could think of would be if you wanted to maintain a history of store managers. You wouldn't just want a store_id and manager_id, you'd also want a started_at and ended_at timestamp. This would allow you to view who is managing a store right now, but also who managed a store in the past.
With Eloquent, these types of extra fields don't require their own model, they can be accessed through the various methods documented above.

What is the right Laravel class name for a given table name?

If I have database table suggestions_votes, what would be the correct name of Laravel (5.1) Class (SuggestionsVote or SuggestionVote)?
Table was created by migration, using
Schema::create('suggestions_votes', ...
Laravel recommends certain conventions, but they also provide you with options to override them.
If your model is "SuggestionVote", then the table associated with that model will be the snake case plural name of the class. In other words, it would look for the table "suggestion_votes". If you want to override the associated table name, you can add this property to your model:
protected $table = 'suggestions_votes';
If you are actually creating a pivot table for the models "Suggestion" and "Vote", then Laravel will by convention join the two related model names in alphabetical order. In other words, it will look for the pivot table "suggestion_vote". You can override this though when you define the relationship. For example:
return $this->belongsToMany('App\Suggestion', 'suggestions_votes');
Where 'App\Suggestion' would be fully namespaced path to your Suggestion class.
It depends. You can make any name work.
If you have no control of the database, the model 'should' be SuggestionsVote
If you do have control over the database, I would rename the table to suggestion_votes and the model name would be SuggestionVote

Categories