Laravel Pivot table id column - laravel

In Laravel and Eloquent you can use ManyToMany-Relations with a pivot table. The problem in my case is the automatic ID of the pivot row. Can I change how the ID is generated? I want to use UUIDs for that matter.
For other Objects you can just implement this behaviour in the Model, but there is no model for pivot objects.
Did I miss something?

Yeah, wherever you are adding new records to the pivot table, either through attach() or sync(), you give it a key/value array of additional items to put into the pivot table as the second parameter.
So for example $user->roles()->attach($role, array('id' => $uuid));
If you are doing this, it might be useful to also make sure your id isn't set to auto increment.
It might also be important to note that a lot of people don't even have an id column on their pivot table because it's usually not required unless you plan on creating a model for it, or it contains some other foreign key for some reason besides the 2 it would usually have.

I think creating Intermediate model does better job otherwise we will have to add primary uuid whenever we create new record.
use Webpatser\Uuid\Uuid;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProjectAssignee extends Pivot
{
public $incrementing = false;
protected $keyType = 'string';
protected $primaryKey = 'id';
protected $guarded = [];
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->id = (string) Uuid::generate(4);
});
}
}
Now we can create attach easily
Project::first()->assignees()->attach(Assignee::inRandomOrder()->take(5)->pluck('id')->toArray());
Don't forget to chain using method to tell relationship about intermediate table.
public function assignees(){return $this->belongsToMany(
Assignee::class,
'projects_assignees',
'project_id',
'assignee_id')
->using(ProjectAssignee::class);}

Be careful here : id on a pivot table can cause confusion when doing a join, as it will end up on the same row as joined table's id, and can cause fairly nasty bugs, as the latter will override the former. If you really need one, at least change its name.

Related

Laravel Eloquent Many to Many Relationship pivot table

I have three tables categories, film_categories and films and three models respectively Category, FilmCategory and Film.
I have seen many tutorials where they don't create a pivot table model like i have created FilmCategory Model . They just create a pivot table film_categories without a model.
My question is what is the best practise - ?
Should i create a FilmCategory model and set a hasMany relationship
class Film extends Model
{
use HasFactory;
protected $primaryKey = 'film_id';
/**
* Film film Relationship
*
* #return Illuminate\Database\Eloquent\Relations\HasMany
*/
public function categories()
{
return $this->hasMany(FilmCategory::class, 'film_id', 'film_id');
}
}
OR
Should i just create a pivot table film_categories without a model FilmCategory and set belongsToMany relationship
class Film extends Model
{
use HasFactory;
protected $primaryKey = 'film_id';
/**
* Film film Relationship
*
* #return Illuminate\Database\Eloquent\Relations\HasMany
*/
public function categoriesWithPivot()
{
return $this->belongsToMany(Category::class, 'film_categories', 'film_id', 'category_id');
}
}
Technically speaking, both option are fine. You can implement the two of them, and depends on what you want to implement/achieve in the logic part of the code, use one between the two that suits best. Here are things to consider:
First, depends on what is film_categories table used for. If the table is simply exists to relate films and categories tables using many-to-many relationship, then there's no need to create the FilmCategory model, and you can just use belongsToMany(). Otherwise, if the film_categories will relates to another table, then you should create FilmCategory model and define the relation using hasMany(). You're also likely need to add a primary-key field to film_categories if this is the case.
The second consideration to take is what kind of data structure you want to have. Using the codes that you provide, you can get Films using these 2 queries and it'll gives you the correct values but with different structures:
Film::with('categoriesWithPivot')->get();
// Each record will have `Film`, `Category`, and its pivot of `film_categories` table
// OR
// Assuming that `FilmCategory` model has `belongsTo` relation to `Category` ...
Film::with('categories', 'categories.category')->get();
// Will gives you the same content-values as the above, but with in a different structure
And that's it. The first point is mostly more important to consider than the second. But the choice is completely yours. I hope this helps.
Since the film_categories table does not represent an entity in your system, I think you should define a belongsToMany relationship and should not create a separate model for the pivot table. If You still wanna have a model for your pivot table so create a model that extends Illuminate\Database\Eloquent\Relations\Pivot class. Check documentation here: model for pivot table

laravel eloquent migrations

In laravel eloquent relationship, is it still necessary to make migration even though there's an existing database? beginners here.
I create a one-to-one eloquent relationship inside my model to get the specific column from another table's record and fetch to the datatable, but it did not work.
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Directorystatus extends Model
{
use HasFactory;
protected $table = 'user_status';
protected $fillable = ['status_id' , 'status_xtitle'];
public function userbasic() {
return $this->belongsTo(directorybasic::class,'user_xusern','status_xuser');
}
}
class Directoryuser extends Model
{
use HasFactory;
protected $table = 'user_basic';
protected $primaryKey = 'user_id';
protected $fillable = ['user_id' , 'user_xusern' , 'user_xfirtname' ,'user_xmiddlename','user_xlastname'];
public function userstatus() {
return $this->hasOne(directorystatus::class,'user_xusern','status_xuser');
}
}
No. Migrations are not necessary. Defining relationships on both sides is also not necessary, if you don't need them both. (You can have only belongsTo, without having hasOne or hasMany in the opposite model.)
First, make sure you are using the right object (Directorystatus::class / Directoryuser:class - I see they're not capitalized in your code). The next param is the foreign key, meaning the column which points to a model's primary key. The third param is optional and is used only if the primary key is not id.
For example if you have a column status_xuser in the table user_status, which contains a user_id from user_basic table, you should define it like this:
public function userbasic() {
return $this->belongsTo(Directoryuser::class,'status_xuser','user_id');
}
And in order to use this relationship, when you retrieve a model from the db, for example, you should call on it the same way your relationship function is named.
$status = Directorystatus::find(1);
$user = $status->userbasic();
I would also suggest you name your classes in camelCase, because it's the accepted practice (in Laravel especially).

Laravel - How to query a relationship on a pivot table

My Setup
I have a many-to-many relationship setup and working with my database. This is using a pivot table that has an extra column named "linked_by". The idea of this is that I can track the user that created the link between the other 2 tables.
Below is an attempted visual representation:
permissions -> permissions_roles -> roles
permissions_roles -> persons
The Issue
The permissions_roles table has an addition column names "linked_by" and I can use the ->pivot method to get the value of this column. The issue is that it only returns the exact column value. I have defined a foreign key constraint for this linked to persons.id but I can't manage to work out a way to query this from a laravel Eloquent model.
The Question
How do I query the name of the person linked to the "linked_by" column form the Eloquent query?
Ideally, I would like the query to be something like:
permissions::find(1)->roles->first()->pivot->linked_by->name;
BUT, as I haven't defined an eloquent relationship for this pivot table column I can't do this but I can't work out how I would do this if it is even possible?
Is the only way to do this to do:
$linkee = permissions::find(1)->roles->first()->pivot->linked_by;
$person = person::find($linkee);
$person->name;
->using();
I have discovered that Laravel has a way to do what I wanted out of the box by creating a model for the pivot table.
This works by adding ->using() to the return $this->belongsToMany() model function.
By putting the name of the newly created pivot model inside the ->using() method, we can then call any of the functions inside this pivot model just like any other eloquent call.
So assuming that my permissions belongs to many roles and the pivot table has a 3rd column named "linked_by" (which is a foreign key of a user in the Users table):
My permissions model would have:
public function roles()
{
return $this->belongsToMany('App\roles','permissions_roles','permissions_id','roles_id')
->using('App\link')
->withPivot('linked_by');
}
and the new link model would contain:
Notice the extends pivot and NOT model
use Illuminate\Database\Eloquent\Relations\Pivot;
class link extends Pivot
{
//
protected $table = 'permissions_roles';
public function linkedBy()
{
return $this->belongsTo('App\users', 'linked_by');
}
}
Obviously you would need to define the opposite side of the belongsToMany relationship in the roles model, but once this is done I can use the following to pull the name of the person that is linked to the first role in the linked_by column:
permissions::find(1)->roles->first()->pivot->linked_by->name;
You should be able to achieve that in toArray method in Permission model.
/**
* Convert the model instance to an array.
*
* #return array
*/
public function toArray(): array
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
// Detect if there is a pivot value and return that as the default value
if (isset($attributes['pivot']['linked_by']) && is_int($attributes['pivot']['linked_by'])) {
$linkeeId = $attributes['pivot']['linked_by'];
$attributes['pivot']['linkee'] = Person::find($linkeeId)->toArray();
//unset($attributes['pivot']['linked_by']);
}
return $attributes;
}

How do I declare a composite primary key in the Lumen Model?

I have a table with three primary keys, I'm trying to create a model for it, and I'd like to use the find () function, but it throws the error:
Code:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Visit extends Model
{
protected $table = 'ft_visit';
protected $primaryKey = ['sk_time', 'sk_region', 'sk_device'];
protected $fillable = [
'sk_time', 'sk_region', 'sk_device', 'ds_page',
];
public $timestamps = false;
public function time()
{
return $this->belongsTo(Time::class, 'sk_time');
}
}
Error:
(1/1) ErrorException
mb_strpos() expects parameter 1 to be string, array given
You can try the solution presented in this article Solved: Eloquent doesn’t support composite primary keys.
Adding the setKeysForSaveQuery method below on your model, and removing the $primaryKey attribute, you might be able to add this otherwise not supported functionality to your Eloquent model.
<?php
use Illuminate\Database\Eloquent\Builder;
class Visit extends Model {
public $incrementing = false;
protected function setKeysForSaveQuery(Builder $query)
{
$query
->where('sk_time', '=', $this->getAttribute('sk_time'))
->where('sk_region', '=', $this->getAttribute('sk_region'));
->where('sk_device', '=', $this->getAttribute('sk_device'));
return $query;
}
}
EDIT: As noted by #Devon this might affect Eloquent in other ways and should be tested thoroughly before use. However, this should give you some clearance on the way to solve it, should you not be in a position where you can (preferably) restructure the application or data.
Eloquent does not support composite keys. The best thing to do in this scenario to ensure Eloquent compatibility is to convert your existing primary composite key to a unique composite key and add an autoincrement primary key (id) for Eloquent to use.
Unfortunately you can't. Laravel and lumen (using Eloquent) do not support composite keys.

working with m:m relationships in eloquent outside laravel

I have been able to set up a m:m relationship in eloquent outside laravel and retrieve records but I have no idea how to add a new record as you dont create the pivot table in the code.
If these are my classes, how would i add a new instance to the M:M relationship between author and publisher?
<?php
include 'eloquent_database.php';
class Publisher extends Illuminate\Database\Eloquent\Model {
public $timestamps = false;
protected $table = 'publisher';
protected $primaryKey = 'publisher_id';
public function authors (){
return $this->belongsToMany('Author', 'author_publisher', 'publisher_id', 'author_id');
}
}
class Book extends Illuminate\Database\Eloquent\Model {
public $timestamps = false;
protected $table = 'book';
protected $primaryKey = 'book_id';
public function author() {
//related table name, pk in current table,
return $this->belongsTo('Author', 'author_id');
}
}
// Create the company model
class Author extends Illuminate\Database\Eloquent\Model {
public $timestamps = false;
protected $table = 'author';
protected $primaryKey = 'author_id';
public function books()
{
//related table, fk IN related table,
return $this->hasMany('Book', 'author_id');
}
public function publishers (){
return $this->belongsToMany('Publisher', 'author_publisher', 'author_id', 'publisher_id');
}
}
I would also need to know how to delete one too. I have seen this documentation http://laravel.com/docs/4.2/eloquent#working-with-pivot-tables but i dont really follow it
Id really appreicate an exxample of how to add and delete a new instance. Also onine there seeems to be so many different versions its hard to find which docs to follow as the code i have works but i didnt get it from the docs - just trial and error
Thanks a lot in advance
edit:
in repsonse to Rays comment i do have a pivot table in the database called author_publisher with author id and publisher id but i have no idea how to work with this pivot table. Do i have to create a class for it? I dont really understand
Here is a reference to said table in the code above
return $this->belongsToMany('Author', 'author_publisher', 'publisher_id', 'author_id');
When having a M:M relationship, make sure you have a table that translates the relationship between author and publisher. An example table could be composed of entries including both author_id and publisher_id. From what you have provided, you lack such a table.
There's another case if you do not have such a table. In order for such a M:M relationship to work, the author table must contain a column called "publisher_id" for simplicity. Likewise the publisher table must contain a column called "author_id". Then in the author model, return $this->hasMany('Publisher', 'author_id') and in the publisher model, return $this->hasMany('Author', 'publisher_id'). You should get the correct answer.

Resources