I'm quite new to Yii development and I find myself stuck at the very beginning..
In my project I have a table with two columns both pointing to an external table.
Something like:
table "item":
- user_id_1
- user_id_2
table "user":
- id
So an item i always associated with two users, but I do not want to differentiate them, and I want to be able to do something like
$item->users
getting both the users.
And also the opposite:
$user->items
getting all items that reference the user id in user_id_1 or user_id_2.
Does this sound right? Any suggestion?
Yii's ActiveRecord is really only set up to handle one-to-one and one-to-many relationships - you seem to be describing a one-to-two relationship.
One way to handle this would be to set up a MANY_MANY relationship, but just use it with two users instead of "many". You will need to set up a user_item table that connects Users to Items, instead of embedding the key directly in the tables. If you need to enforce only two Users-to-an-Item you'll need to write some extra Validation code in your Item model. The relation in Item will look something like this, and you will be able to call $item->users like you want:
public function relations() {
return CMap::mergeArray(parent::relations(),array(
'users'=>array(self::MANY_MANY, 'User', 'user_item(item_id,user_id)'),
));
}
The new associative table will look like this:
user_item:
-- user_id
-- item_id
If you really need to use two separate user_id foreign key fields like you have here, you will need to set up an ActiveRecord HAS_ONE relation for each, like so:
public function relations() {
return CMap::mergeArray(parent::relations(),array(
'user1' => array(self::BELONGS_TO, 'User', 'user_id_1'),
'user2' => array(self::BELONGS_TO, 'User', 'user_id_2'),
));
}
But, you can still enable the $item->users call syntax with a function something like this, which will return both users in a single array:
public function getUsers() {
return array(
$this->user1,
$this->user2,
);
}
This takes advantage of Yii's __get() method override, which will allow you to call $item->getUsers() like $item->users.
You can set up something similar in the User model, with two HAS_MANY relations for user_id_1 and user_id_2, and a getItems method which returns the merged result arrays.
But I think the easiest option will be to set up a MANY_MANY relationship (and use it a ONE_TWO relation).
Related
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?
User
uid
Provider
pid
Resolution
rid
ProviderResolution
prid
pid
rid
active
ProviderResolutionUser
prid
uid
class Provider extends Model {
public function resolutions()
{
return $this->belongsToMany('App\Models\Resolution')->withPivot('active')->withTimestamps();
}
}
class Resolution extends Model {
public function providers()
{
return $this->belongsToMany('App\Models\Provider')->withPivot('active')->withTimestamps();
}
}
class User extends Model
{
}
Trying to create a Eloquent relationship with this.
I'm trying to figure out how to fit user into this model. It seems like it's suppose to belongsToMany. Do I need to create a class that represents the pivot?
Then from the case of the User how would I query a list resolutions?
You didn't ask but I personally think it's a lot easier to let the primary key of each table be 'id.' Also, in the case of ProviderResolution, unless you have a specific case for it, you don't need (and shouldn't use) 'prid' at all. Just 'pid', 'rid' and 'active' should be sufficient. The 'pid' and 'rid' make the composite primary key on their own. If you add yet another key ('prid'), then there will be a three-key composite which will technically enable you to have duplicates with your other two primary keys. Yuck. Example: PRID:1, PID:1, RID:1, then PRID:2, PID:1, RID:1. Now you have duplicates but your record is technically still unique because of the PRID key. But, maybe you want it this way for some reason?
For the answer I'm going to assume you are using Laravel 5.4+.
So first off, you don't need a class for the pivot. And secondly, you are currently trying to create a relationship between the user and the existing pivot table between Provider and Resolution by creating a table called 'provider_resolution_user'. If you want to query resolutions for a user, just use the relationship methods which gives you access to the attributes on the pivot table and the related models/tables.
First, setup the 'hasMany' relationships in both classes: Users and Resolutions (Providers already has a relationship to Resolutions, so you can use that relationship if you want to see the related Provider.) Then you'll need a pivot table called 'resolution_user'. Put the 'uid' and the 'rid' in the table. Make the relationships to the corresponding foreign key fields to their parent tables.
Now you can access the relationship directly like:
$user->resolutions->rid (or whatever the attribute is you want)
The previous example assumes you have already created a way to insert records into the pivot table (resolution_user) that relate the user and the resolution together.
If you want to access one of the attributes on the pivot table, 'pivot' creates an object instance with it's own attributes (from the table). You can access it like this:
$user->resolutions->pivot->active;
Of course, these methods are chainable so if you just wanted to see the active resolutions, you could also add a ->where statement.
Hope that helps and wasn't too muddy. I'm happy to clarify any points if need be.
EDITED ANSWER:
Because what you want to do is to disable a row in the provider_resolution table and have that reflect on the correct user, then just create a relationship in both the User model and the Resolution model. So when you disable a row in provider_resolution (pid, rid, active), you can lookup the appropriate user to update by using the inverse relationship between resolution and user. This should give you the user that is assigned to that particular resolution/provider combination. If for some reason you do need to find the user based on a unique combination of the TWO: resolution AND provider, then we might need to talk about polymorphic relationships. Let me know.
I want to ask whether is a option to make polymorphic connections between 2 tables based on id and type with pivot table like this:
Where table TABLE_* can be for example TABLE_BOOKS or TABLE_MAGAZINE or whatever.
description:
=> - "has"
... - "other attributes"
FK - "foreign key"
database:
TABLE_* => ID, FK_TYPE_ID,...
SHELFS_WINDOW => ID, FK_TABLE_*_ID, FK_TYPE_ID,...
TYPE => ID,...
Where is SHELFS_WINDOW for one book so there is polymorphic One to One relationshop. I'm able to reach the TABLE_* only with ID and type of the table from document for example. But the type of TABLE_* is in another table with some other informations.
Now I want to know whether is there any posibility to create this relationship with a clean way. Yes, I know, I can do it with \DB:: class. I want to stay in database with SELECTion and I don't want to lose functionality from the returned object Builder.
So I'm just asking whether is there any possibility to do it or not. Or maybe another approach.
Thank You for help!
EDITED:
Create select with \DB class and select desired table (even with multiple joins) without returning object and create this object array then set on the model guarded =[ ] and set $fillable to all then create a new Object model and fill it with created array. Then u have to set exists = true and return nice Eloquent object.
You can implement the function on your eloquent model,
eloquent will use this function to determine the table that should be used
public function getTable() :string
{
//perform your id / type specific table logic here
return $tablename;
}
So here's my problem.
I need to link an insurance policy to the insured property/item. Now the details vary greatly from car policy to a house or business one. So what I want to do is have something like this on the policies table
Policies
item_id
item_type
and that links to different tables depending on the value of the field "item_type" for example:
item_type = car then link to the cars table
item_type = house then link to the houses table
item_type = business then link to the businesses table
and so on...
I can do that on my own with php and mysql but I want to know the proper way to do it using CakePHP's table relationships and linking. I tried using the through option and a relationship table but it's not the same. Any ideas? or if a relationship table is the only way to do it then tell me how please.
This is actually a lot simpler than it first appears. I've done this a few times so I'll detail the technique that I use.
The first thing to do is create a behavior. This will allow you to enable any Table class in your application to have a policy attached to it.
The behavior is very simple. I've changed it to match your example, as I understand it. I'll talk through it after the code.
namespace App\Model\Behavior;
use Cake\Event\Event;
use Cake\ORM\Behavior;
use Cake\ORM\Query;
class PolicyBehavior extends Behavior
{
public function initialize(array $config)
{
parent::initialize($config);
$this->_table->hasMany('Policies', [
'className' => 'Policies',
'foreignKey' => 'table_foreign_key',
'bindingKey' => 'id',
'conditions' => ['table_class' => $this->_table->registryAlias()],
'propertyName' => 'policies'
]);
}
public function beforeFind(Event $event, Query $query, \ArrayObject $options, $primary)
{
$query->contain(['Policies']);
return $query;
}
}
So the in the initialize method we need to create a relationship to the table we attached the behaviour to. This will create a Table hasMany Policies relationship, meaning that any item in your system can have many policies. You can update this relationship to match how you're working.
You can see that there are a number of options defined in the relationship. These are important, as they link the tables items together. So the table_foreign_key is a field in your policies db table used to store the primaryKey of the related item. So if you're attaching a Policy to a Car, this would be the Car.id. The bindingKey is the key used in the Policy table to join on.
In order to filter the different types of attachments, you need the table_class field in your policies db table. This will be the name of the attached table class. So Cars, Cats, Houses etc. Then we can use this in the conditions, so anything pulling the primary table class will automatically filter the related Policies to match.
I've also configured the propertyName, this means that any item you look for which contains Policies will have an entity property called policies with the related data inside.
The last function in the behaviour is the beforeFind, this just ensures that whenever you look for the primary table class, you always return the related policies, you don't have to use this if you don't want to, but I found it handy to always have the related data in my use-case.
So then, how do we use this new behaviour? Just attach it like you would any other behaviour, and that's it. $this->addBehavior('Policy').
Be aware
This just reads data, you'll need to ensure that you save the table alias, and the foreignKey into the related table when creating new items.
Just for clarity, your policies table schema will need, at a minimum.
policies.id
policies.table_class VARCHAR(255)
policies.table_foreign_key INT(11)
I have two tables in MySQL, where the first one is called users and the second one is called games. The table structure is as follows.
users
id (primary)
email
password
real_name
games
id (Primary)
user_one_id (foreign)
user_one_score
user_two_id (foreign)
user_two_score
My games table is holding two foreign relations to two users.
My question is how do I make the model relations for this table structure?? - According to the laravel documentation, I should make a function inside the model and bind it with its relations
for instance
public function users()
{
$this->belongsTo('game');
}
however I can't seem to find anything in the documentation telling me how to deal with two foreign keys. like in my table structure above.
I hope you can help me along the way here.
Thank you
A migration:
$table->integer('player1')->unsigned();
$table->foreign('player1')->references('id')->on('users')->onDelete('cascade');
$table->integer('player2')->unsigned();
$table->foreign('player2')->references('id')->on('users')->onDelete('cascade');
And a Model:
public function player1()
{
$this->belongsTo('Game', 'player1');
}
public function player2()
{
$this->belongsTo('Game', 'player2');
}
EDIT
changed 'game' to 'Game' as user deczo suggested.
Unfortunately the way you have this setup is not likely to work in the current context. You may have more luck with the belongsTo method, but again that only supports one relationship.
You could implement a user1() belongsTo, a user2() belongsTo and finally just declare a non eloquent function to return both (something like $users = array($this->user1(), $this->user2())