CakePHP 3.0 Timestamp behavior - cakephp

I would like to update a timestamp in a users table when a user logs in. I created a datetime field called 'lastLogin'.
From my users controller's login action I call:
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
$this->Users->touch($this->Users->get($user['id']), 'Users.afterLogin');
}
And in my Users table I have:
$this->addBehavior('Timestamp', [
'events' => [
'Model.beforeSave' => [
'created' => 'new',
'modified' => 'always',
],
'Users.afterLogin' => [
'lastLogin' => 'always'
]
]
]);
I have tested that the event is triggered and entity property is being updated. However it is not saved to the database.
Is this intended, i.e. do I have to explicitly save the entity, or am I missing something?
Thanks!
Andrius

The behavior only updates the field
It isn't really clear from the documentation, but the code only updates the field value. The behavior won't actually update the db, it's effectively assumed there would be a call to save later in the same request.

Related

How to add link to show action of the relation entity on relation field in SonataAdminBundle

Im making admin panel in SonataAdminBundle.
In User show action i have field companies which return array of companies assigned to the user.
It is a OneToMany relation. UserCompany has user_id and company_id.
I want to create link on each returned company name, which points to it's entity show action.
This is code from configureShowFields() function in UserAdmin class:
->with('Assigned organizers',['class' => 'col-md-6'])
->add('companies', null, [
'label' => 'Organizers',
])
->end()
I managed to create a link on a string field pointing to show action of an entity, but the id property is taken from the current entity view:
->with('Address', ['class' => 'col-md-6'])
->add('userProfile.locality', 'url', [
'route' => [
'name' => 'admin_app_employee_show',
'identifier_parameter_name' => 'id'
],
'label' => 'Localiy',
])
What's more Sonata Admin create links on related fields, when the relation is direct,
for example:
Company has many Employee. Then in Company show action on employees field
I see array with links already heading to edit action of Employee entty.
Maybe there is a possibility to override template for this field, but it seems unclear for me,
as the documentation lacks of more advanced examples.
This is how I tried to test overriding the template of a field:
->add('userProfile.street', null, array(
'label' => 'Street',
'template' => 'custom-field.html.twig',
))
Location of the template: App/templates/Admin/
Any help appreciated
SonataAdmin automatically creates links to related entities once it has all of them configured and added to services.
Then you can just change the route action of the link on the relation field as following:
->with('Assigned events', ['class' => 'col-md-6'])
->add('events', null, [
'route' => [
'name' => 'show'
],
'label' => 'Events',
])
->end()
You can also change the type of relation field eg.'many_to_one' instead of null which might help in some cases.

CakePHP 3.x - Save many-to-many data

I am attempting to patchEntity with a join table, but I am unable to get it to save the associated records, and I think I have the backend code correct, but I am unsure of what the frontend code should look like...
Here is the scenario, I have three tables
Ledgers Table
id int
title string
Tribes Table
id int
name string
LedgersTribes Table
id int
ledger_id int
tribe_id int
Here is my backend code
public function ledgerSave($id = null)
{
$this->loadModel('Ledgers');
$ledger = $this->Ledgers->get($id, [
'contain' => ['LedgersTribes']
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$ledger = $this->Ledgers->patchEntity($ledger, $this->request->getData(), [
'associated' => ['LedgersTribes']
]);
if ($this->Ledgers->save($ledger)) {
$this->Flash->success(__('The ledger has been saved.'));
return $this->redirect($this->referer());
}
$this->Flash->error(__('The ledger could not be saved. Please, try again.'));
}
$this->set(compact('ledger'));
}
Here is the relevant frontend code
<?= $this->Form->control('tribe_id[]', ['type' => 'checkbox', 'label' => false, 'class' => 'minimal', 'value' => $tribe->id]) ?>
My question is, what should the field name be for the tribe_id, the idea is, i have a list of checkboxes and the user checks off a couple of boxes and then those tribe_id's get inserted into the LedgersTribes table with the ledger_id
Any ideas on how I can do this?
EDIT: Here is a screenshot of the form
I have reviewed the following links, and none of them answer my question...
CakePHP 3: Save Associated Model Fail
Save associated in cakephp 3 not working
How to save associated joinData in cakephp 3.x
CakePHP 3 cannot save associated model
Cakephp 3 - Save associated belongsToMany (joinTable)
This should do:
echo $this->Form->control('tribes._ids[]', [
'type' => 'checkbox',
'label' => false,
'class' => 'minimal',
'value' => $tribe->id]
]);
This is described here: https://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data
I think you have to get the table (ex: TableRegistry::getTableLocator()->get('')) where you want to save the data. Then create entity from that and save the data, hopefully it will work.

Validate Laravel Nested Rules in Array

I'm with Laravel and I want to write elegant validation rules :) With this Framework its really easy, but I don't know how to approach this when facing 1:n relationships.
I have two Resources, User and Contact. An User can have multiple Contacts.
So, I want a Form where you can fill all User's fields AND all Contact's information.
To do that, I would like to write a Request like this:
UserRequest:
public function rules()
return [
'name' => 'required|string',
'email' => 'required|email|unique:exists:users,id',
'contacts' => 'array',
'contacts.*' => new ContactRequest() // This is the problem
]
My question is: How can I apply this type of validation? Specifically when using array, how can I make a Modular Validation to apply validations of nested Resources? Or should I develop a ContactRule instead?
Edit:
I want that front end send form like this:
` // POST: users
{
'name': 'UserName',
'email': 'user#mail.com'
'contacts': [
[
'email' => 'contac_1#mail.com',
'contact_type_id => 1
],
[
'email' => 'contac_2#mail.com',
'contact_type_id => 2
],
}
`
Thats all,
Thx!
We have an API with 100's of results in each request or perhaps post/patch.
We still use:
'data.relationships.users.data.*.id' => [
'string',
'unique:api_groups,name,' . ($this->route('group')->id ?? 0),
]
So for you
'contacts.*.email' => 'required|email|unique:exists:users,id'
Works perfectly. It doesn't get more complex or anything.

Cakephp 3 - Additional ID-field in belongsToMany association's through table

I have been working on a CakePHP 3 project that has parts on it that are a bit more complex and it seems that the framework's official documentation does not give me the answer I am looking for. A following thing is needed to be done:
Users can belong to multiple Projects. In each Project each User that belongs to the current Project can access into different sections of the application, this is handled by a table called Resources, which stores the resource ids.
So in short, User A can have access to different amount of Resources in Project A and Project B.
I have setup following associations:
Users belongsToMany Projects through UsersProjects
Users belongsToMany Resources through UsersResources
In users_resources -table I have the following fields:
user_id | resource_id | project_id
project_id -field is added to the table to make the user's resources project-specific.
Association configuration is as follows:
UsersTable:
$this->belongsToMany('Resources', [
'foreignKey' => 'user_id',
'targetForeignKey' => 'resource_id',
'joinTable' => 'users_resources',
'through' => 'UsersResources'
]);
ResourcesTable:
$this->belongsToMany('Users', [
'foreignKey' => 'resource_id',
'targetForeignKey' => 'user_id',
'joinTable' => 'users_resources',
'through' => 'UsersResources'
]);
UsersResourcesTable:
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Resources', [
'foreignKey' => 'resource_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Projects', [
'foreignKey' => 'project_id',
'joinType' => 'INNER'
]);
There is also a buildRules-function implemented in the UsersResourcesTable-class to help with the data integrity:
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['user_id'], 'Users'));
$rules->add($rules->existsIn(['resource_id'], 'Resources'));
$rules->add($rules->existsIn(['project_id'], 'Projects'));
$rules->add($rules->isUnique(['user_id', 'resource_id', 'project_id']));
return $rules;
}
The data is saved like this:
$usersTable = TableRegistry::get('Users');
$user = $usersTable->get(129);
$data = [
'resources' => [
[
'id' => 48,
'_joinData' => [
'project_id' => 2
]
]
]
];
$user = $usersTable->patchEntity($user, $data, ['associated' => ['Resources']]);
if ($usersTable->save($user)) {
$this->out('success');
} else {
$this->out('fail');
}
The entity data after patchEntity:
{
"id": 129,
"email": "john.doe#example.com",
"created": null,
"modified": "2016-02-05T20:50:06+0000",
"firstname": "John",
"lastname": "Doe",
"type": 1,
"resources": [
{
"id": 48,
"type": "r",
"_joinData": {
"project_id": 2
}
}
]
}
When the data is saved with project id 2, the save works as expected. When I save the same data but I change the project id to 1 (user_id and resource_id will remain the same), the previous row with project id 2 is deleted from the table. The desired behaviour would obviously be that the older record would remain in the table. I noticed though that adding a saveStrategy-option with value 'append' to UsersTable's Resources-association definition will make the save go through correctly (the row with project_id 2 will not be deleted). But when I try to save an existing record while append-strategy is used, the save will fail (the save result is false).
So if I use replace-strategy, previous data gets deleted and if I use append-strategy, the update of an existing row will result to an error in cake's perspective. I think the error is thrown during rules checking due to the unique-rule.
I think the replace strategy would be the way to go but it seems that the project_id -field value gets ignored and it leads to the situation that previous data (with same user_id and resource_id) will be deleted whenever a new row is added with a different project_id.
So my question is:
Is it possible to add a third id-field to belongsToMany-association's trough-table so that the extra field would also be considered as an id-field so the framework would know when to update or insert records to the join-table?
The idea is that all the three fields in the table would be unique. Now it seems that the framework discards the extra field so inserts and updates do not work as expected. Or is there a design flaw in the database structure that is needed to be fixed?
I would prefer sticking with the CakePHP-conventions but it seems this scenario is quite hard to tackle. Any guidance is appreciated!

How to retrieve information when linking models through a table in CakePHP 3.0?

I'm trying to set up a user access model based on the one in the CakePHP blog tutorial (http://book.cakephp.org/3.0/en/tutorials-and-examples/blog-auth-example/auth.html), but with Roles in a separate table, and linked to users by a UserRoles table.
I currently have the following in Model/Table/UsersTable.php:
$this->belongsToMany('Roles', [
'through' => 'UserRoles'
]);
and the following in Model/Table/RolesTable.php:
$this->belongsToMany('Users', [
'through' => 'UserRoles'
]);
and the following in Model/Table/UserRolesTable.php:
$this->belongsTo('Users', [
'foreignKey' => 'user_id'
]);
$this->belongsTo('Roles', [
'foreignKey' => 'role_id'
]);
I have view, create, and administrator roles created. I'm trying to figure out how to check a user's role or roles in AppController.php. This is the simple example given for when the role incorporated into the user object:
public function isAuthorized($user)
{
// Admin can access every action
if (isset($user['role']) && $user['role'] === 'admin') {
return true;
}
// Default deny
return false;
}
I'm not sure how to access the User object and get the user's role by the user's ID from the AppController file. Since the user is not linked to the role directly, how would I access the role information from an IsAuthorized function? How would I do a lookup to retrieve the user's role when it is linked by another table?
Thank you!
When setting up the AuthComponent in your controller, make sure you tell it to fetch related data:
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'contain' => ['Roles']
]
]
]);
}

Resources