I'm newbie with CakePHP and need some help. I baked my first application but I'm not able to make it behave as I want.
I have two tables, players and teams, and a join table players_teams. The form displays fine. But I would like to change the select area to a drop down menu. The player could have many teams over time (I added a from and a to date columns to the join table for that), but I would like the player to be assigned to one team at the moment it is created.
Here is what I have tried:
<?php
echo $this->Form->input('player_name');
echo $this->Form->input('player_last_name');
echo $this->Form->input('player_number');
echo $this->Form->input('player_birthday');
echo $this->Form->input('teams._ids', [ 'multiple' => false, 'options' => $teams,]);
?>
It does not change to a dropdow. I have also tried this:
echo $this->Form->input('teams._ids', ['type' => 'select', 'multiple' => false, 'options' => $teams, 'empty' => true]);
This last one change the select to a dropdown, but whatever I select doesn't go to the database.
Heres the Controller for Players:
public function add()
{
$player = $this->Players->newEntity();
if ($this->request->is('post')) {
$player = $this->Players->patchEntity($player, $this->request->data);
if ($this->Players->save($player)) {
$this->Flash->success(__('The player has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The player could not be saved. Please, try again.'));
}
}
$teams = $this->Players->Teams->find('list', ['limit' => 200]);
$this->set(compact('player', 'teams'));
$this->set('_serialize', ['player']);
}
Here's the PlayersTeamController:
public function add()
{
$playersTeam = $this->PlayersTeams->newEntity();
if ($this->request->is('post')) {
$playersTeam = $this->PlayersTeams->patchEntity($playersTeam, $this->request->data);
if ($this->PlayersTeams->save($playersTeam)) {
$this->Flash->success(__('The players team has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The players team could not be saved. Please, try again.'));
}
}
$players = $this->PlayersTeams->Players->find('list', ['limit' => 200]);
$teams = $this->PlayersTeams->Teams->find('list', ['limit' => 200]);
$this->set(compact('playersTeam', 'players', 'teams'));
$this->set('_serialize', ['playersTeam']);
}
Any help will be really appreciated. Thanks!
Have you tried:
echo $this->Form->select(
'teams'
,$teams->toArray()
,[
'multiple' => false,
'class' => '_ids',
'empty' => true
]
);
Just resemble the required data structure yourself, that is
[
'teams' => [
'_ids' => [
// single id here
]
]
]
Cookbook > Database Access & ORM > Saving Data > Converting BelongsToMany Data
You can do that by for example specifying the proper element name
attribute value, like
echo $this->Form->input(
'teams',
[
'name' => 'teams[_ids][0]'
//'options' => $teams // this is optional when following the conventions
]
);
which will automatically choose a select
type, based on the options.
You will need some additional logic to prevent further selections to be injected, as an array-ish name like teams[_ids][0]
would allow that without the Security component being able to detect such tampering. This can be done using validation, or by preparing the data in the Model.beforeMarshal
event.
Here's a validation example
public function validationCreate(Validator $validator) {
$validator = $this->validationDefault($validator);
$teamsValidator = (new Validator())
->requirePresence('_ids')
->notEmpty('_ids')
->add('_ids', 'valid', [
'rule' => function($value) {
return is_array($value) && count($value) === 1;
}
]);
$validator
->requirePresence('teams')
->notEmpty('teams')
->addNested('teams', $tagsValidator);
return $validator;
}
$player = $this->Players->patchEntity($player, $this->request->data, [
'validate' => 'create'
]);
Another options would be to specify a non array-ish name, combined with preparing the data in the Model.beforeMarshal
event, like
echo $this->Form->input('teams', ['name' => 'initial_team']);
public function beforeMarshal(Event $event, \ArrayObject $data, \ArrayObject $options)
{
if (isset($data['initial_team'])) {
$data['teams'] = [
'_ids' => [
$data['initial_team']
]
];
}
}
Cookbook > Database Access & ORM > Saving Data > Modifying Request Data Before Building Entities
Both will retain the CakePHP magic like the marshaller converting the passed _ids
into entities so that the associations are being saved properly, automatically picking up the input options, automatically choosing the input type, etc...
If you have made proper associations, you can save directly from the PlayersController. PlayersTeamController isn't needed at all. When you patch the newly created player entity with the request data, associated table data will be automatically get into it and saved by the entity save() command as its one level of association only.