Search code examples
cakephpormsaveassociationscakephp-3.0

Saving data using where clause


I would like to be able to save data while providing a where clause.

I have the following code:

$game = $this->Games->newEntity();
if ($this->request->is('post')) {
    $game = $this->Games->patchEntity($game, $this->request->data);
    if ($this->Games->save($game)) {
        $this->Flash->success(__('The game has been saved.'));

        return $this->redirect(['action' => 'index']);
    } else {
        $this->Flash->error(__('The game could not be saved. Please, try again.'));
    }
}

I tried this, but it didn't work:

$this->Games->save($game)->innerJoinWith('Leagues', function ($q) use($s) {
                    return $q->where(['Leagues.user_id' => $s]);
                }
            )

I suspect having to do a find with a where clause and patch the request data to it?

$game = $this->Games->find()->innerJoinWith('Leagues', function ($q) {
    return $q->where(['Leagues.user_id' => $this->Auth->user('id')]);
})
->where(['Games.id' => $id])
->first();

if(! count($game)){
    $this->Flash->error(__('The game could not be found.'));
    return $this->redirect(['action' => 'index']);
}

if ($this->request->is('post')) {
    $game = $this->Games->patchEntity($game, $this->request->data);
    if ($this->Games->save($game)) {
        $this->Flash->success(__('The game has been saved.'));

        return $this->redirect(['action' => 'index']);
    } else {
        $this->Flash->error(__('The game could not be saved. Please, try again.'));
    }
}

Anything simpler?


Solution

  • There is no such saving syntax, retrieving the proper entity in beforehand is the correct way to go, and there isn't really a "simpler" way - however there surely is room for optimization.

    Personally I'd use finders to DRY things up, ie create a finder on the GamesTable that restricts things to specific league users:

    public function findForLeagueUser(\Cake\ORM\Query $query, array $options)
    {
        if (!isset($options['id'])) {
            throw new \InvalidArgumentException('The `id` option is invalid or missing.');
        }
    
        return $query
            ->innerJoinWith('Leagues', function (\Cake\ORM\Query $query) use ($options) {
                return $query->where([
                    $this->Leagues->aliasField('user_id') => $options['id']
                ]);
            });
    }
    

    and combine things in the controller action:

    $game = $this->Games
        ->find('forLeagueUser', ['id' => $this->Auth->user('id')])
        ->where(['Games.id' => $id])
        ->first();
    

    You could also use firstOrFail() instead, which throws an exception if the record cannot be found, this is what Table::get() does internally too.

    And you could reduce things even further by using a dynamic finder for the games id:

    $game = $this->Games
        ->findById($id)
        ->find('forLeagueUser', ['id' => $this->Auth->user('id')])
        ->first();
    

    See also