In my app there is a Transaction table with:
seller_id, buyer_id and asset_id.
seller_id and buyer_id are id's supposed to point to Users table. To stick to the convention and keep automatic associations both should be called "user_id" (which is of course impossible)
What is the correct way to create such associations in CakePHP 3.x?
My guess is that I should create special association tables:
Sellers (id, user_id)
Buyers (id, user_id)
and then associations would be trough those tables:
Transaction => Sellers, Buyers => Users
Is that correct? Would it work? Is there a better way?
You can define the relationship with different alias and foreign keys like below.
In your transactions model/Table.
$this->belongsTo('Sellers' , [
'foreignKey' => 'seller_id',
'className' => 'Users'
]);
$this->belongsTo('Buyers' , [
'foreignKey' => 'buyer_id',
'className' => 'Users'
]);
If you also want to define the relationaship in user model, you can define this in this way.
In User model/table
$this->hasMany('BuyerTransactions' , [
'foreignKey' => 'buyer_id',
'className' => 'Transactions'
]);
$this->hasMany('SellerTransactions' , [
'foreignKey' => 'seller_id',
'className' => 'Transactions'
]);
Related
We are currently on CakePHP 3.7.5, using MariaDB as our database engine (10.4.12 on dev, 10.2, 10.2 on client machines).
Some of our table classes have a lot of belongsTo associations. Many of them pointing to a contacts table, but even more to an options table. We use this table to store arbitrary values for different properties of models so that the clients can manage these lists themselves, add entries or change their textual representation while keeping the referenced id.
Main Table: entities
id INT(11)
name VARCHAR(256)
administrator_contact_id INT(11)
account_manager_contact_id INT(11)
property_manager_contact_id INT(11)
...
type_id INT(11)
classification_id INT(11)
status_id INT(11)
category_id INT(11)
...
contacts table
id INT(11)
first_name VARCHAR(256)
last_name VARCHAR(256)
options table
id INT(11)
name VARCHAR(256)
EntitiesTable class
class EntitiesTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->addAssociations([
'belongsTo' => [
'AdministratorContact' => [
'className' => 'Contacts',
],
'AccountManagerContact' => [
'className' => 'Contacts',
],
'PropertyManagerContact' => [
'className' => 'Contacts',
],
...
'Type' => [
'className' => 'Options',
],
'Classification' => [
'className' => 'Options',
],
'Status' => [
'className' => 'Options',
],
'Category' => [
'className' => 'Options',
],
...
],
...
]);
}
}
Example query in a Controller
$this->loadModel('Entities');
$query = $this->Entities->find('all', [
'conditions' => [
...
],
'contain' => [
'AdministratorContact',
'AccountManagerContact',
'PropertyManagerContact',
...
'Type',
'Classification',
'Status',
'Category',
....
],
]);
$results = $query->toArray();
Now, for the first time, we ran into MariaDBs limit of 61 JOINs. Our belongsTo associations are added using the default "join" strategy as it reduces the overall number of queries that have to be executed for serving a single request. Changing the strategy of all belongsTo associations to "select" would lead to way too many, unnecessary separate queries.
I was wondering if anybody heard of best practises, or whether there is a behavior that caters to this need. Things that come to my mind:
Before running a query, analyse how many associations are contained which would lead to JOINs, plus anything else that would lead to additional JOINs ("join" clause in the query, LinkableBehavior). If it exceeds the limit, turn some of them into separate SELECTs by amending their "strategy".
Somehow aggregate JOINS coming form belongsTo associations pointing to the same table by collecting the ids, and after the query has run "distribute" the results. Kind of what is done for hasMany associations, but there the results are turned into entities which are then all added as one property (containing an array of results), whereas for belongsTo we would then need to distribute the results back to different properties of the main model.
I have a table that has a "hasOne"-association to another table, but without any foreign key. So my "hasOne"-association finds the related entity with a customized finder-method:
$this->hasOne('HourlyRates', [
'foreignKey' => false,
'finder' => ['byShift' => ['userId' => '1', 'shiftBegin' => '2016-01-01']]
]);
My problem is, that the options for the finder have to be the attributes of the actual entity of the first table. This is the user_id of one entity and the shift-begin of one entity.
I tried
'finder' => ['byShift' => ['userId' => 'Shifts.user_id', 'shiftBegin' => 'Shifts.begin']]
But it didn't work.
So do you know, how to access the attributes of one entity in the Table-class?
Hello there,
thanks in adv for help.
i have two table: named as options fields are (id,option_title,created,status) and second table as option values fields as (id,option value,options_id,created,status). THe option values table has a foreign key as options_id. Now when i created association between these two table . i didn't work.. Let me explain you how i am creating association.
In the Option model i am using following code.
var $hasMany = array(
'values' => array(
'className' => 'Optionvalue',
'foreignKey' => 'options_id'
));
But this is not working and i am not able to get data from options table. Can any please advice me.. where i am wrong. I just need options table data in the optionsvalue add view file.
Assuming you have a table called options and another called optionvalues. Here is how the Option model looks like
class Option extends AppModel {
public $hasMany = array(
'Optionvalue' => array(
'className' => 'Optionvalue',
'foreignKey' => 'option_id',
'dependent' => FALSE
)
);
}
In the Optionvalue model you then go like this
class Optionvalue extends AppModel {
public $belongsTo = array(
'Option' => array(
'className' => 'Option',
'foreignKey' => 'option_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
This is how it should look like. In the Optionvalue Add method, you will need to write this in the view
echo $this->Form->create('Optionvalue');
echo $this->Form->input('option_id');
echo $this->Form->end('Save');
and the options will appear by themselves.
Very important though, you did not follow the naming conventions. options_id needs to be option_id among others.
Use
option_id in place of 'options_id'
Cakephp strongly follows naming conventions.
http://book.cakephp.org/2.0/en/getting-started/cakephp-conventions.html
Also looks like you are not using proper naming conventions for other places too.
i need some help with these, I can't seem to be able find anything on internet.
I have a model
public $belongsTo = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
),
'Audit_type' => array(
'className' => 'Audit_type',
'foreignKey' => 'audit_type',
),
'User' => array(
'className' => 'User',
'foreignKey' => 'auditor',
),
);
but I need to be able to find Group with a like, because now in group_id i have multiple id like 145,125,123
is there any way i can find it with a LIKE??
Update:
the table groups is a tree behavior table, is tree behavior from cakephp
now the table that has multiple ids, is a table that has audits, and i need this audit to be part of multiple groups.
DON'T store multiple id's in the group_id field!
Rather than a BelongsTo relationship, it sounds like you need CakePHP's Has And Belongs To Many relationship.
But whatever you do, don't store multiple id's in the one foreign key field. That would be enough to give Codd a heart attack!
You shouldn't use a "LIKE" for finding multiple IDs.
Do it like this instead:
$this->Group->find('all', array(
'conditions' => array(
'id' => array('145', '125', '123')
)
);
It will use MySQLs "IN".
I have this model and controller and it returns fine if it is a hasOne relationship but now DishCategory hasMany dishes. When I change the code below to hasMany it gives me an error saying Dish.id is not a known column while if it is a hasOne it works fine. How can I make it so that it returns all the Dish.id's that have id=1? (still using the join).
class DishCategory extends AppModel{
public $hasOne = array(
'Dish' => array(
'className' => 'Dish',
'foreignKey' => 'dish_category_id'
)
);
}
class DishCategoriesController extends AppController {
function get_categories($id)
{
// find category with a dish of $id
$this->set('dishes', $this->DishCategory->find('all', array(
'conditions' => array(
'Dish.id' => $id
)
)));
// set master layout
$this->layout = 'master_layout';
}
}
hasOne relations can be joined into the primary SQL query. hasMany relations cannot and need to be queried in a separate query. The conditions you specify for the query only apply to the primary query, all separate relational queries are build just based on the ids retrieved in the primary query. Set debug to 2 and have a good look at the query log to see what I mean.
To find the category of a dish, fetch the dish and look at its related category record:
$dish = $this->Dish->find('first', array('conditions' => array('Dish.id' => $id)));
echo $dish['DishCategory']['name'];
Since I take it that a dish belongsTo one category, your query "find all categories which have a dish with id x" makes little sense; there should only be one anyway.