1. ukisoft

    No comment

    ukisoft
Changes in tags
Changes in body
Source | HTML | Preview

※すみません、ウソ書いたかもしれません。ちょっと検証中・・・。

知ってますか?cakePHP3のTableにfindOrCreateというメソッドがあることを!
見つかったらEntityを、見つからなければ作って保存して、でEntityを返してくれるニクいやつです。
なぁ、僕は知らなかったわけで・・。

ソースコード

こんな感じです。

Table.php
public function findOrCreate($search, callable $callback = null)
    {
        $query = $this->find()->where($search);
        $row = $query->first();
        if ($row) {
            return $row;
        }
        $entity = $this->newEntity();
        $entity->set($search, ['guard' => false]);
        if ($callback) {
            $callback($entity);
        }
        return $this->save($entity) ?: $entity;
    }

引数として、where()中身を渡すことができます。
['id' => 10]みたいな配列のことですね。
DBにデータがなければ、それを元にEntityを作ってくれます。
何か足りなければ、callbackで更なる加工をすれば良いと思います。
ただし、entityに再度代入してないので、callbackの引数に'&'をつけてentityを加工する必要があるかと。

おまけ:saveのreturn

returnで三項演算子が使われてるのですけど、パッと見て、save()に成功したらnullでも返すの?と勘違いしてしまいました。
調べたら、公式にも書いてありました。

PHP 5.3 以降では、三項演算子のまんなかの部分をなくすこともできるようになりました。 式 expr1 ?: expr3 の結果は、expr1 が TRUE と同等の場合は expr1、 それ以外の場合は expr3 となります。

つまり、save()の結果がtrueだったら、trueを・・・あれ?Entityがほしいんですけど。
で、更に調べると、どうもsave()は、falseかEntityを返すっぽいです。
ずっと、trueかfalseを返すものだと思ってた・・・。

Table.php
public function save(EntityInterface $entity, $options = [])
    {
        $options = new ArrayObject($options + [
            'atomic' => true,
            'associated' => true,
            'checkRules' => true,
            'checkExisting' => true,
            '_primary' => true
        ]);

        if ($entity->errors()) {
            return false;
        }

        if ($entity->isNew() === false && !$entity->dirty()) {
            return $entity;
        }

        $connection = $this->connection();
        if ($options['atomic']) {
            $success = $connection->transactional(function () use ($entity, $options) {
                return $this->_processSave($entity, $options);
            });
        } else {
            $success = $this->_processSave($entity, $options);
        }

        if ($success) {
            if (!$connection->inTransaction() &&
                ($options['atomic'] || (!$options['atomic'] && $options['_primary']))
            ) {
                $this->dispatchEvent('Model.afterSaveCommit', compact('entity', 'options'));
            }
            if ($options['atomic'] || $options['_primary']) {
                $entity->isNew(false);
                $entity->source($this->registryAlias());
            }
        }

        return $success;
    }

思いの外、色々勉強になりました。