Is there any way you could implement composite primary keys using Models in CodeIgniter 4?
Like this?
class SomeModel extends Model{
protected $table = 'table';
protected $primaryKey1 = 'primary_composite_id1';
protected $primaryKey2 = 'primary_composite_id2';
protected $primaryKey3 = 'primary_composite_id3';
// numbers in the identifiers were only added for clarity
...
}
I think you can define the table structure using the Forge class. Here is a snippet of how I defined the table in Migrations.
in SomeMigration.php
class SomeClass extends Migration{
/* added fields here */
...
// Set them as foreign keys
$this->$forge->addForeignKey('item_id', 'item', 'item_id', 'CASCADE', 'CASCADE');
$this->$forge->addForeignKey('poster_uid', 'user', 'user_id', 'CASCADE', 'CASCADE');
$this->$forge->addForeignKey('customer_uid', 'user', 'user_id', 'CASCADE', 'CASCADE');
// Set them as primary keys
$this->$forge->addPrimaryKey('item_id');
$this->$forge->addPrimaryKey('poster_uid');
$this->$forge->addPrimaryKey('customer_uid');
...
}
Also, if setting composite primary keys in Models are not possible, should I
Create a new primary key column for that table instead?
Leave out the $primarykey value as empty and use just use queries (e.g. using WHERE)?
Use any one of the columns(set as PK) in the table as the value for $primarykey?
questions were created based on this post Codeigniter MY_Model composite primary key, since the answer did not directly answer the question
I am currently using the framework for our school project. There was none mentioned in the documentation, so I got stuck. Any kind of alternative solution is very much appreciated. Thanks! Cheers!
I have run into the same problem these days.
It seems that Codeigniter doesn't support this functionality for the model's primary key.
I searched in documentation and in Model's source code and I saw that it handles the primary key like one value.
e.g. at models find method I can see this code:
$row = $builder->whereIn($this->table . '.' . $this->primaryKey, $id)
In my case I will add an extra ID column and use this as a primary key. If I find a better alternative in the future I will update here.
Best Regards
Related
I'm quite new to laravel and I'm trying to understand the Eloquent Relations.
I've already read some answers and the documentation but I haven't found a simple case similar to mine.
I have two model with one-to-many relation.
Document Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
public function dossiers()
{
return $this->belongsTo('App\Dossier');
}
protected $table = 'documents';
protected $primaryKey = 'id_document';
}
Dossier Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Dossier extends Model
{
public function documents()
{
return $this->hasMany('App\Document');
}
protected $table = 'dossiers';
protected $primaryKey = 'id_dossier';
}
So there is an attribute "protocol" inside both table. Inside Dossier is an unique attribute, inside Document can be repeated because more documents may be part of same dossier with that protocol.
Let's suppose I have to create a Document Model. But I need an attribute "color" that already exists in a Dossier.
so:
I have a form for the document. That can set all attribute except
color (so it can create the protocol shared with Dossier)
I submit the form then check if exists a Dossier with that protocol
(that is not the primary_key)
If i find a Dossier with that protocol, I took the Color of that dossier and
I insert it in the Document Model.
I'd like to know how I should implement the second step.
At the moment I've wrote in the DocumentController:
$document = Dossier::where('protocol', '=', $request->protocol)->first();
and then
$document -> color;
But I fell that's not the way.
Thanks for any advice
I think you want to insert color in document from dossier table if protocol match. If i understand you correctly then you can write it like this.
$dossier = Dossier::where('protocol', '=', $request->protocol)->first();
$color = ($dossier) ? $dossier->color: 'defaultColor';
Now in your create document
$document -> create([
'color' => $color,
... other form data here
]);
Note: it is not good practice to use primary key field as id_document or id_dossier. Better you change these to id, otherwise your current relationship will not work. Default, laravel relationship assume id as primary key. If you want to use different primary key name then you need to pass that key name in relationship as a second parameter.
$this->hasMany('App\Comment', 'foreign_key', 'local_key');
https://laravel.com/docs/5.6/eloquent-relationships#one-to-many
There are manyway to write the relation code, one is as rkj said anad u did as well.
The other one which i personally prefer is
$this->hasManny(Comment::class,'foreign_key', 'local_key'); //This is only if you are not following laravel standard table syntex.
But if your local key is id and foreign key on another table is comment_id. Then you dont need to add foreign key and primary key. You can simply do like this
$this->hasManny(Comment::class);
For making query, You can simply do this
$dossier = Dossier::where('protocol', $request->protocol)->first(); //This will give object
You do not need = , if its other then that u need to add that. This way its looks much cleaner right?
Then to display you can simply do this $dossier->color
I have a Tournament model which contains a belongsToMany relationship with a Session model:
class Tournament extends Model{
public function sessions(){
return $this->belongsToMany(Session::class, 'tournament_has_sessions');
}
}
Then my sessions model contains a belongsToMany relationship with the User model:
class Session extends Model{
public function users(){
return $this->belongsToMany(User::class, 'session_has_users');
}
}
Now when I delete a Tournament, I want to delete all sessions and all information in the session_has_users table with it.
I have tried:
$tournament->sessions()->with('users')->delete(); -- < This
$tournament->sessions()->delete();
$tournament->users()->detach();
$tournament->delete();
But it does't work. The data in the session_has_users table persists
I realize i could do something like this:
DB::table('session_has_users')->whereIn('session_id', $this->sessions()->pluck('id'))->delete();
But is there a more efficient/handy (or even alternative if not more efficient) way to accomplish this?
Using RDBMS Referential Actions on the Foreign Key
In the database schema, you should define the foreign key with the ON DELETE CASCADE action. This will automatically delete the related records when the referenced id is deleted.
(See dbudimir's answer for the example lines from a Laravel migration file)
Using Eloquent to Detach All Related Models
If you aren't using foreign keys or your database lacks the support for referential actions, you can use this:
$tourament->sessions()->sync([]);
The sync method accepts an array of IDs to place on the intermediate table. Any IDs that are not in the given array will be removed from the intermediate table. So, after this operation is complete, only the IDs in the given array will exist in the intermediate table:
https://laravel.com/docs/5.5/eloquent-relationships#updating-many-to-many-relationships
Providing an empty array should then remove all the relations.
In the table 'tournament_has_sessions' you can set onDelete('cascade') like this
$table->integer('tournament_id')->unsigned()->nullable();
$table->foreign('tournament_id')->references('id')->on('tournaments')->onDelete('cascade');
$table->integer('session_id')->unsigned()->nullable();
$table->foreign('session_id')->references('id')->on('sessions')->onDelete('cascade');
And when delete tournaments automaticly deleted all records in this table
you can use any of these:
$tourament->sessions()->detach();
or
$tourament->sessions()->sync([]);
or you can define the foreign key with the ON DELETE CASCADE like the answers above
I am trying to use a table for my Users and separate table for users' Projects in my database. However I want the names of the fields to be different for user id. What I want to take the id from the 'Users' table; and while saving the created project to the database, use that (user) id as created_by_id in Projects table.
public function store(CreateProjectRequest $request)
{
$project = new Project($request->all());
Auth::user()->projects()->save($project);
// Project::create($request->all());
return redirect('pages/home');
}
Also in Users.php, I added:
public function projects()
{
return $this->hasMany('App\Project');
}
The commented field is working on its own. However, I guess my problem arises because when I comment that line again, and add the other two lines ($project... and Auth::user... bits), I guess it is assuming I have a field in the Projects table named id.
I thought I would work around this problem with changing the primary key but I couldn't find how to take the Auth::user()->id; and make it write that value in created_by_id in a secure way. This is what I found though:
class Project extends Eloquent {
protected $primaryKey = 'created_by_id';
}
Edit: I don't think changing the primary key is my solution.
You can pass a second and third parameter to hasMany() method to specify the keys to use. Documentation
public function projects()
{
return $this->hasMany('App\Article','userid','created_by');
}
I have a relation I'm trying to get working that seems to be searching by the wrong column. I have a model, userwords, that should be getting an associated word with it. I want it to be using the word_id column from the userword table to search for a word by the id in the word table, but instead it seems to be using the id of the userword row to search for the word. I thought that perhaps that if I told it which column to use in the third parameter of hasOne() it would work, but to no avail. The code in question is:
public function word(){
return $this->hasOne('Word', 'id', 'word_id');
}
any help or ideas would be appreciated! Also if you need more information, please just let me know and i'll update this here! Thanks a lot!
Your parent table is userword and related child table is word, in this case, Userword model should contain following method for making the relation with word table:
class Userwords {
protected $table = 'userword';
public function word(){
return $this->hasOne('Word', 'userword_id'); // use the foreign key here
}
}
In this case, your word table should contain the userword_id as a foreign key. So, if you have a different foreign key defined word table then use that foreign key in the place of userword_id.
Also remember that, tables should use plural name of the word, for example, words should be the table name but you used word and the Model should use a singular name, for example, Word for the words table, so you have a different name convension here so use the protected $table = 'word' in your Word model and in Userwords model use protected $table = 'userword'. So, finally, it should be something like this:
class Userword {
// change the table name in database (use userwords)
protected $table = 'userwords';
public function word(){
return $this->hasOne('Word', 'userword_id'); // use the foreign key here
}
}
For words table, it should be:
class Word {
// change the table name in database (use words)
protected $table = 'words';
public function userwords(){
return $this->belongsTo('Userword');
}
}
Read the manual for more information about Laravel Model Relationships.
I'm working with a base class MY_Model for accessing the database, which contains all the CRUD methods. The model which I'm using is Jens Segers' MY_Model:
https://github.com/jenssegers/CodeIgniter-My-Model
Now, I have a table in my database which contains a composite primary key (article_id and tag_id). Normally, when the primary key contains only an ID for instance, I could just use:
protected $primary_key = "id";
Is there a possibility for a composite primary key, or should I add a column ID to be the only primary key?
Thanks in advance.
Don't sacrifice your database structure because the custom library you are using doesn't support exactly what you need.
There's a work around however:
That library uses CI's database library and uses the $primary_key in the db->where() function.
That function accepts the following argument formats:
$this->db->where('name', $name);
$this->db->where($array);
$this->db->where($where);//$where is a custom MySQL query string
Therefore, you CANNOT set a composite primary key by setting:
$primary_key = array("article_id", "tag_id");
as the library constantly uses the first method of the where() method above.
Instead, just supply methods like get() or insert() (or other) each time with array("article_id" => $article_id, "tag_id => tag_id).
The database will throw an error if you try and insert anything matching your primary key, so you can deal with that then.