I am having a bit of a problem with saveAll, however I have a feeling what I am trying to do isn't possible but would be interested to see what people think.
The models in question are
League
Team
User
The relationships are as follows:
LEAGUE
One league can have many teams, however the league is owned by one user (the admin).
var $belongsTo = 'User';
var $hasMany = 'Team';
TEAM
Each team can be in one league and be owned by one user.
var $belongsTo = array('User', 'League' => array('counterCache' => true));
USER
Each user can be admin for multiple leagues and also have multiple teams
var $hasMany = array('League', 'Team');
I have a form where by a user can create a new league, create a team to add to the league and also register. The data that gets posted back to CakePHP is as follows:
array(
'League' => array(
'name' => 'Valid League Name',
'max_teams' => '10'
),
'Team' => array(
array(
'name' => 'Valid Team Name'
)
),
'User' => array(
'password' => 'password1',
'firstname' => 'Firstname',
'lastname' => 'Lastname',
'email' => 'test#example.com',
'confirm_password' => 'password1'
)
);
Now assuming this all passes validation and this is the first data entered in to the database I would expect the following to be returned from the saveAll operation that I am using:
array(
'League' => array(
'id' => 1,
'name' => 'Valid League Name',
'max_teams' => '10',
'user_id' => 1
),
'Team' => array(
array(
'id' => 1,
'name' => 'Valid Team Name',
'league_id' => 1,
'user_id' => 1
)
),
'User' => array(
'id' => 1,
'password' => 'password1',
'firstname' => 'Firstname',
'lastname' => 'Lastname',
'email' => 'test#example.com',
'confirm_password' => 'password1'
)
);
What is happening however is that the user_id field in the Team array isn't getting populated and it just coming back instead as null. What I am guessing is that CakePHP can't be sure that the team and the user that have just been created are necessarily related to one another so haven't created the association between the two.
At the moment, within the controller where the save is being made (I am saving through the League model $this->League->saveAll()) I am manually getting the user_id once everything has been saved and resaving the team with the new user_id. Is there anything I can do to get Cake to realise that the two are associated to the saveAll function does everything I need? I have tried using saveAll() with the deep option enabled and this also has no affect.
Related
It's probably a very basic problem, but I can't wrap my head around it how to solve this properly.
In my application, I've got users who have multiple addresses with different purposes. For example, a user can have an Address and an InvoiceAddress. I want to store both addresses in the same table (the Address model) and then assign them to one User.
The respective models look something like:
Address:
public $belongsTo = array(
'Address' => array(
'className' => 'Address',
),
'InvoiceAddress' => array(
'className' => 'Address',
),
);
And the User:
public $hasOne = array(
'Address' => array(
'className' => 'Address',
),
'InvoiceAddress' => array(
'className' => 'Address',
),
);
When a user would have one address, I'd add a user_id as foreign key to the Address model and be done with it. But obviously, something has to be done differently for CakePhp to be able to distinguish between the address and the invoice address.
In my mind the solution is something like adding a address_id and invoice_address_id to the User table, but I can't seem to get it working in any way.
If you want to add in your user table the related fields then it is better to add them in the following form address_user_id and the invoice_address_user_id. Then you must have a belongsTo relation in your user model that looks like:
public $belongsTo = array(
'Address' => array(
'className' => 'Address',
'foreignKey' => 'address_user_id',
),
'InvoiceAddress' => array(
'className' => 'Address',
'foreignKey' => 'invoice_address_user_id',
),
);
So when you execute the following command $this->User->find('all'); the result will be
$result = array(0 =>
array('User' => array(...),
'Address' => array(...), // it will bring only one address row
'InvoiceAddress' => array(...) // it will bring only one address row
),
1 => ...
2 => ...
);
Then in your address Model the relationships you must add are:
public $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'address_user_id',
),
'InvoiceUser' => array(
'className' => 'User',
'foreignKey' => 'invoice_address_user_id',
)
);
I am having problems with the CakePHP belongsTo and hasAndBelongsToMany relationships in CakePHP 2.x
Example situation
Table users
id
organisation_id
Table organisations
id
name
Table user_organisation_permissions
id
user_id
organisation_id
UserModel
hasAndBelongsToMany(Organisation);
belongsTo(Organisation)
A user belongs to one organisation but it has permissions on multiple organisations, resulting in the following conflict:
$aUser = $this->User->findById(1);
print_r($aUser);
// Output
# With the belongsTo relation
array(
'User' => array(
'id' => 1,
'organisation_id' => 1
'name' => 'Test User'
),
'Organisation' => array(
'id' => 1,
'name' => 'Test organisation'
)
);
# With the hasAndBelongsToMany relation
array(
'User' => array(
'id' => 1,
'organisation_id' => 1
'name' => 'Test User'
),
'Organisation' => array(
1 => array(
'id' => 1,
'name' => 'Test organisation'
),
2 => array(
'id' => 1,
'name' => 'Test organisation'
)
)
);
# When both relations are enabled it doesn't work
Does anybody have a solution for this conflict?
Is there a "native" CakePHP solution for this conflict?
The answer is actually in the CakePHP 2.x Cookbook.
Multiple relations to the same model
There are cases where a Model has more than one relation to another Model. For example, you might have a Message model that has two relations to the User model: one relation to the user who sends a message, and a second to the user who receives the message. The messages table will have a field user_id, but also a field recipient_id. Now your Message model can look something like:
class Message extends AppModel {
public $belongsTo = array(
'Sender' => array(
'className' => 'User',
'foreignKey' => 'user_id'
),
'Recipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id'
)
);
}
Source: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#multiple-relations-to-the-same-model
I have a database:
Companies | Users | Files
- ID - ID - ID
- company_id - user_id
So for my models:
Company hasmany User
User belongsto Company & hasmany File
File belongsto User
How can I list al the files belonging to a company? Without the user and company data? So a single array listing files.
I could fetch all the user IDs followed by a query in File searching with these IDs. But I was wondering, is there a best practice cakePHP way?
I'm fairly new to cake.
Thanks
As far as I know it's not possible to get company files without any user or company data, but you can limit the data by using the Containable behaviour. For example, if you want to get files that belong to a company in the company controller you could use this:
$users = $this->Company->User->find('all', array(
'conditions' => array('User.company_id' => $id),
'fields' => array('id', 'company_id'),
'contain' => array('File'),
));
The result ($users) would look something like this:
array(
0 => array(
'User' => array(
'company_id' => '75',
'id' => '51'
),
'File' => array(
0 => array(
'id' => '399',
'user_id' => '51',
...
),
1 => array(
'id' => '337',
'user_id' => '51',
...
)
...
)
)
1 => array(
'User' => array(
'company_id' => '75',
'id' => '65'
),
'File' => array(
0 => array(
'id' => '450',
'user_id' => '65',
...
),
...
)
)
)
You can then get an array of files using the Hash utility.
$files = Hash::extract($users, '{n}.File.{n}');
Which will give you something like this:
array(
0 => array(
'id' => '399',
'user_id' => '51',
...
),
1 => array(
'id' => '337',
'user_id' => '51',
...
),
3 => array(
'id' => '450',
'user_id' => '65',
...
),
...
)
Don't forget to enable the behaviour. I suggest you do it in your app model by adding the following line:
public $actsAs = array('Containable');
CakePHP will not help you on this and even if want to write mysql query you cant do this by using files table only until and unless you will add company_id in files table.
As I understood from the cakephp-documentation, one of the advantages of the 'containable'-Behavior is being able to fetch fewer data if you need fewer data...
But that doesn't seem to work in my case of a connection between users and usergroups.
My associations look like:
Group
hasMany: Membership
User
hasMany: Membership
Membership
belongsTo: User, Group
(I'm not using HABTM, instead use the Model 'Membership' in between to join users and groups).
All Models implement the 'Containable'-Behavior.
Now I want to get all the members of a group with a certain id, only their ids and mail-addresses. My query is built like that:
$members = $this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
),
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
));
But the resulting array looks like:
array(
(int) 0 => array(
'Membership' => array(
'id' => '1',
'group_id' => '1',
'user_id' => '1',
'role' => 'member'
),
'Group' => array(
'id' => '1',
'name' => 'Foo Group'
),
'User' => array(
'password' => '*****',
'id' => '1',
'name' => 'Dr. Foo'
'mail' => 'foo#bar.baz',
'role' => 'admin'
)
)
)
So there are definietely more fields fetched than I wanted to... (it's the same thing btw wenn I set the 'contain'-key to:
'contain' => array(
'User.fullName', 'User.id'
)
Am I using the containable-behavior wrong?
Your models don't seem to be acting containabl-y at all. Have you set your models to act as containable?
class Post extends AppModel {
public $actsAs = array('Containable');
}
If so, maybe the problem is with the recursion (to avoid getting the Group array with the query). Containable behavior should handle the recursion level on its own, but try setting it on the AppModel just to be sure
class AppModel extends Model {
public $actsAs = array('Containable');
public $recursive = -1;
Your first attempt
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
looks good in terms of syntax, so it probably the actAs thing.
Also, for debugging also, try
$this->Membership->contain('User');
$this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
));
and see if you get the expected results that way.
This might be a bit strange, but I'll try to explain. Basically I have a table containing users and a table containing customers. Users have permission to look into the data of only certain customers. So I figured I would make a separate table for user permissions, taking the user ID and the customer ID as foreign keys and having one row per customer/user permission.
When the admin adds a new user to the database using a class called UserForm (posted shortened below for reference), which uses Zend\Form, I want to also display all the customers next to the form as buttons that can be selected to be added as permissions. Now, I thought I would do that by having a JavaScript array that appends or removes the customer IDs if they're selected/deselected, then passing that array to the form as a hidden value and finally looping through the array inserting a row into the permissions table for each customer ID in the array, and taking the user ID that's been created as well. I'm not sure if this is the best way to do it, but it's the best I could come up with.
Hope that's at least slightly understandable. So, I have one form, but I want to insert into two different tables. My question, I guess, is how do I pass the array to the form as a value? And how do I insert into not only the users table, but also the permissions table, when the saveUser() method is called (I'll post that below too). Also, is this a really weird way of doing it that I'm being unnecessarily difficult about? I'd love to hear if there's a much easier way.
My UserForm class:
namespace Admin\Form;
use Zend\Form\Form;
class UserForm extends Form
{
public function __construct($name = null)
{
parent::__construct('user');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'userId',
'attributes' => array(
'type' => 'Hidden',
),
));
$this->add(array(
'name' => 'activated',
'attributes' => array(
'value' => 1,
'type' => 'Hidden',
),
));
$this->add(array(
'name' => 'username',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Username:',
),
));
$this->add(array(
'name' => 'firstname',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'First name:',
),
));
$this->add(array(
'name' => 'lastname',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Last name:',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Go',
'id' => 'submitbutton',
),
));
}
}
My saveUser() method
public function saveUser(User $user)
{
$data = array(
'firstname' => $user->firstname,
'lastname' => $user->lastname,
'username' => $user->username,
'activated' => $user->activated,
);
$userId = (int)$user->userId;
if ($userId == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getUser($userId)) {
$this->tableGateway->update($data, array('userId' => $userId));
} else {
throw new \Exception('User ID does not exist');
}
}
}
One method is to use Form Fieldsets. A form can have multiple Fieldsets, and each Fieldset can be bound to a different entity.
These are useful when you have a one to one relationship between the entities
You would then create teo entities, for example a User entity (as you already have) and a new entity to represent your Permission.
You would create a Feildset for each and bind the objects to the fields sets. (UserFieldSet and PermissionFieldset for example)
checkout the section on Form Fieldsets:
http://zf2.readthedocs.org/en/latest/modules/zend.form.advanced-use-of-forms.html
If you have a one to many relationship, ie. One User can have Many Permissions, then you would be better off looking at form Collections:
http://zf2.readthedocs.org/en/latest/modules/zend.form.collections.html
there's an example of dynamically adding new rows for a one to many relationship too.