CakePHP : Multiple hasOne Model Relations - cakephp

I'm trying to create an application which should help distribute work to employees or volunteers on a irregular time schedule based on their availabilities.
Anyway, here are my models and the relations between them :
Users hasMany Jobs
Jobs belongTo Users
Users hasMany Availabilities
Availabilities belongTo Users
Jobs hasOne Periods
Periods belongTo Jobs
Availabilities hasOne Periods
Periods belongTo Availabilities
The problem is that "Periods" is on the receiving side of two hasOne relationships and, if I'm not wrong, it's something you can't do in CakePHP. What is the best way to proceed in this situation?
You might have also spotted the fact a "Job" may be assigned to someone (a "User") or not. Should I drop the relationship and create it once the job has been assigned or should I create a fictional user representing "nobody" for unassigned jobs?
I would have posted a nice image but i don't have enough reputation, sorry!
Here's a link though.

The problem is that "Periods" is on the receiving side of two hasOne relationships and, if I'm not wrong, it's something you can't do in CakePHP. What is the best way to proceed in this situation?
I don't see a problem. You should be able to add the foreign key for each hasOne relationship to the periods table (periods.job_id and periods.availability_id).
You might have also spotted the fact a "Job" may be assigned to someone (a "User") or not. Should I drop the relationship and create it once the job has been assigned or should I create a fictional user representing "nobody" for unassigned jobs?
This is fine. Just set the jobs.user_id to allow null values and set up the relationship. This will allow jobs to be created without being assigned to users. After you perform a find you can check empty($results['User']) to determine if you are dealing with an unassigned job.
Users hasMany Jobs, Jobs belongTo Users, ...
Just a reminder: model names should be singular (ie. User hasMany Job, Job belongsTo User, etc.)
Also I'd like a Period to belong to either a Job or an Availability and never both at the same time. Is there a conventional way to do it?
You can write validation for that in the Period model:
$validate = array(
'job_id' => array(
array(
'rule' => 'validBelongsTo',
'message' => 'A period can either belong to a job or an availability'
),
),
);
public function validBelongsTo() {
$hasJob = !empty($this->data['Period']['job_id']);
$hasAvailability = !empty($this->data['Period']['availability_id']);
if ($hasJob && $hasAvailability) {
return false;
} else {
return true;
}
}

Sounds to me like the Period model should be polymorphic. Check out the answer to this question and this article in the bakery.
I used this technique frequently and it works beautifully for this kind of thing.

Related

cakephp database table defining three dependent relationships

I have three tables (blocks, roles, users) where the following is true in the schema:
blocks have many users
users have many blocks
users have many roles, but the roles are specific to the block (and the user)
blocks have many roles, but the roles are specific to the user (and the block)
For example:
user => john
belongs to block => 'metro' with role => 'builder'
belongs to block => 'rural' with role => 'investor'
user => dan
belongs to block => 'metro' with role => 'resident'
belongs to block => 'rural' with role => 'investor'
I have a join table named block_role_user with the following columns:
id block_id role_id user_id created modified
I guess this would be a hasandbelongstomany relationship amongst all three tables. I thought about laying this out with a hasandbelongstomany table between blocks and users and another hasandbelongstomany table between roles and users but this will not fully define the unique relationship between a block, role and user.
I'm looking for advice on the best way to approach this and appreciate any advice.
I've looked into binding the model, but this doesn't seem to apply for the unique three part relationship I have here.
I thought about creating a model called blockroleuser and setting up the relationships using standard cakephp convention. If this is an acceptable way, can you give some guidance on naming the model and controller files?
Thanks again...
If I got you right, then you are looking for a relation less loose than HABTM, which will define a specific connection between a User, a Block and a Role.
Personally I would probably use a model that defines these relations using belongs to, where a User can have many of those, something like:
User hasMany Whatever
Whatever belongsTo User
belongsTo Block
belongsTo Role
This is probably more or less what you ment when you were talking about a BlockRoleUser model, the proper naming might depend on what exactly blocks are. Of course a generic name like for example Involvement or even UserBlockRole might be fine too, I don't think you should bother too much about this.
Anyways, this looks pretty straightforward and strict to me, and if necessary for your application, then additional HABTM relations could be set up too utilizing the same table. These HABTM relations would most probably only be used for selects only, for example when there's need for querying all the Blocks a User is related to, or when testing whether a User is related to a specific Block, etc...

One Way One-To-One Association in CakePHP

I have an idea of how to do this but it doesn't seem like proper convention. I have a Submission model and a Revision model each with their similarly named tables. Each Submission can have one or more Revisions associated to it in a $hasMany relationship. The Revision model hence has a $belongsTo relationship linking back to the Submission.
In addition to having this relationship, the Submission model needs to have another association (called activeRevision) to a particular Revision in a $hasOne style of relationship. However, the $hasOne type requires the foreign key to be in the Revision table. I want it to be in the Submission table, so I don't need to query all of the Submission's Revisions to find the active one. I realized just specifying a $belongsTo relationship in the Submission would do what I want, but this feels wrong to me as now the two models "belong to eachother".
Is there a better way to go about this?
I wouldn't worry too much because of the 'naming' of the relation. By having the foreign key of the Revision inside the Submission table, CakePHP is, in fact, right that you've created a 'belongsTo' relation.
Although not strictly the 'right' (?) relation, in this situation, this looks like it's an easy way to achieve what you want. Just be sure to add an additional condition to your relation to prevent that a revision of another submission can be set as the current revision, i.e.;
public $belongsTo = array(
'Revision' => array(
'conditions' => array(
'Revision.submission_id = Submission.id',
)
)
);
However, make sure to add a proper comment in your code to prevent confusion if you're (or somebody else is) looking at your code in a later stage. (e.g. "note: using belongsTo relation, because its easier to maintain)
If you really want to convert it to a hasOne relation, you'll have to add an additional column to the 'Revisions' table, for example 'is_curreny_revision'. However, you will also need to be sure that only one revision of a submission can be set to be the current revision.
If you're using PostgreSQL, this can be achieved using a 'partial unique' index (see this question on StackOverflow; PostgreSQL: Conditional unique constraint). MySQL does not support partial indexes, so you're out of luck there

CakePhp form validation when having two models on two different database

I've a not common problem to do a form validation.
First let me explain a part of the problem: I'm doing a cakePhp website, this website will be used to sell product to customer. To do this, we have two database: one database(database A) relative to products, customer references, bills(provided by the ERP), and one database (database B)relative to information that the website has to store only for the website(passwords of users, cart content, comments on a products, ...).
To register ONE user on our website, I've to:
Create one "address" in the database A
Create one "customer" in the database A
Create one "user" in the database B.
This has to be only one action.
I'm on the user controller, so no problem to validate every fields of the "user", but how to make this form validate all constraints I have in my customer and address models?
The problem is that because user and customer are not in the same database, I can't(in fact I'm not sure of that, but it seems to be logic, because of automatic Left join) declare the $belongsTo and $hasOne relationship between user and customer.
So how could I make the check of those constraints?
Thank you very much
You can validate fields manually.
$this->Customer->set( $this->data );
$this->Address->set( $this->data );
if( $this->User->validates() && $this->Customer->validates() && $this->Address->validates() ) {
// save data
}

How extensive is an Object in CakePHP model linkage?

I was hoping someone with an understanding on CakePHP could shed some light on a question I've been having.
Here's my scenario, I have a User this User has a Company which in turn has many Department and many Address. If I were to get a User could I expect to have access to the Company and all models associated with that Company?
So would $user['Company']['Department'][0] or $user['Company']['Address'][0] be possible?
Which brings me back to the original question, how extensive is the linkage between models?
In plain-vanilla model, Cake's model linkage is determined by your models' recursive attribute. Your example model relationship looks something like this:
User
-> belongsTo Company
-> hasMany Department
-> hasMany Address
-> hasMany PhoneExtension
(I've added an additional relationship (User hasMany PhoneExtension) to flesh out the following explanation.)
There are three accepted values for Model::recursive: -1, 0, 1 and 2. Each value indicates to Cake's ORM a different depth to retrieve model records. I'll use $this->User->find('all') to illustrate the difference.
At recursive = -1, Cake retrieves only the specified model (ie. User). It parses none of the model associations.
At recursive = 0, Cake retrieves the specified model, and parses its belongsTo associations. $this->User->find('all') would retrieve all User records, as well as the Company record to which each User belongs.
At recursive = 1, Cake retrieves the specified model, and parses all of its direct associations. $this->User->find('all') would retrieve all User records, as well as the Company record to which each User belongs, and all PhoneExtension records belonging to the User.
At recursive = 2, Cake retrieves the specified model, parses all of its direct associations and all associations of its direct associations. $this->User->find('all') would retrieve everything in the example model relationship diagram: all User records, the Company records to which the User records belong, all PhoneExtension records belonging to the User, and all Department and Address records belonging to the Company.
Which is the very long way of saying that yes, you can achieve the results you indicate in your question, at recursive = 2.
If you wanted to go deeper than what recursive = 2 gets you, you'll have to use the Containable behaviour. Let's say that your Department model had an additional association: hasMany Group. Thus:
User
-> belongsTo Company
-> hasMany Department
-> hasMany Group
-> hasMany Address
-> hasMany PhoneExtension
To retrieve everything we got with a recursive = 2 retrieval, as well as all the associated Group records, you'd construct your Model::find call like this:
$this->User->find('all', array(
'contain' => array(
'PhoneExtension',
'Company' => array(
'Department' => array( 'Group' ),
'Address'
)
)
));
It's as extensive as you need/want it to be. Look into the recursive option of the find() family of methods. Also the Containable behavior. The specific references you list are possible, but directly under the user:
$user['Department'][0]
Think of it as the user having many departments through its company.
If you access a class/object and set "$this->recursive = -1" then it only returns the object without the dependencies!

CakePHP hasAndBelongsToMany (HABTM) Delete Joining Record

I have a HABTM relationship between Users and Locations. Both Models have the appropriate $hasAndBelongsToMany variable set.
When I managing User Locations, I want to delete the association between the User and Location, but not the Location. Clearly this Location could belong to other users. I would expect the following code to delete just the join table record provided the HABTM associations, but it deleted both records.
$this->Weather->deleteAll(array('Weather.id' => $this->data['weather_ids'], false);
However, I am new to CakePHP, so I am sure I am missing something. I have tried setting cascade to false and changing the Model order with User, User->Weather, Weather->User. No luck.
Thanks in advance for any help.
Not quite sure how Weather is related to your models, so I'll just go with the traditional names, LocationsUser is the joining table. This should delete all associations between the user with id $id and any locations:
$this->User->LocationsUser->deleteAll(array('LocationsUser.user_id' => $id), false);
Notice also that you're missing a closing bracket in your code snippet.

Resources