确保Phalcon相关n-n模型的独特性

时间:2014-09-19 07:24:09

标签: php model phalcon

我的模型contents通过中间表tagscontents_tags有多对多的关系。当我在contents中插入新行时,我还要添加多个标记。虽然这很好,但我希望tags条目是唯一的,因此如果它们是新的,它们将被插入,或者如果它们已经存在则更新(尽管没有任何变化)。

This unit test似乎暗示这可以自动完成,但我无法设法复制相同的行为。如果我的标签表上没有唯一索引,那么我会得到多个相同的条目。如果我这样做,则标签模型会抛出错误。

这是我的测试代码:

$content                = new Content();
$content->title         = 'xkcd';
$content->description   = 'description goes here';
$content->url           = 'http://xkcd.com/';
$content->created_on    = new Phalcon\Db\RawValue('NOW()');
$content->tags          = array();

$tagsText = 'xkcd,comics,testing';

$tags = array();
foreach(explode(',', $tagsText) as $tagText) {
    $tag = new Tag();
    $tag->tag = trim($tagText);
    $tags[] = $tag;
}
$content->tags = $tags;

if($content->save()) {
    $app->response->setStatusCode(201, "Created");
    $app->response->setJsonContent($content->overview());
} else {
    $app->response->setStatusCode(400, "Bad Request");
    $app->response->setJsonContent(array('errors'=>$content->getMessagesAsArray()));
}

内容模型:

class Content {
    public function initialize() {
        $this->hasManyToMany(
            'id',
            'ContentsTags',
            'content_id',
            'tag_id',
            'Tag',
            'id',
            array('alias' => 'tags')
        );
    }

    public function getSource() {
        return 'contents';
    }
}

ContentsTag模型:

class ContentsTags {

    public function initialize() {
        $this->belongsTo('content_id', 'Content', 'id', array('alias' => 'content'));
        $this->belongsTo('tag_id', 'Tag', 'id', array('alias' => 'tag'));
    }

    public function getSource() {
        return 'contents_tags';
    }
}

标记模型:

class Tag {

    public function getSource() {
        return 'tags';
    }

    public function initialize() {
        $this->hasManyToMany(
            'id',
            'ContentsTags',
            'tag_id',
            'content_id',
            'Content',
            'id',
            array('alias' => 'contents')
        );
    }
}

表中的示例数据:

内容:

+----+-------+-----------------------+------------------+
| id | title | description           | url              |
+----+-------+-----------------------+------------------+
| 11 | xkcd  | description goes here | http://xkcd.com/ |
+----+-------+-----------------------+------------------+

contents_tags:

+----+------------+--------+
| id | content_id | tag_id |
+----+------------+--------+
|  1 |         11 |      1 |
|  2 |         11 |      2 |
+----+------------+--------+

标记:

+----+--------+
| id | tag    |
+----+--------+
|  1 | comics |
|  2 | maths  |
+----+--------+

上面提到的单元测试模型似乎没有设置特殊参数,我找不到它们的实际表声明,所以我有点亏。单元测试的模型可以在这里看到:

1 个答案:

答案 0 :(得分:0)

这是我对单元测试正在做什么的误解。我以为它注意到"第1部分"和"第2部分"已经作为零件存在,它实际上做的是注意到它们有一个ID,因此不需要插入。

我将此添加到Tag类:

/**
 * Look to see if a tag exists, if it does then
 * return it. If it doesn't then create it and
 * return it.
 *
 * @param  string $tagName
 * @return Tag    $tag
 */
public static function getOrCreate($tagName) {
    $tag = static::findFirst(
        array(
            'conditions' => "tag=?0", 
            "bind" => array($tagName)
        )
    );
    if($tag) return $tag;

    try {
        $tag = new Tag();
        $tag->tag = $tagName;
        $tag->save();
        return $tag;
    } catch(Exception $e) {
        $this->appendMessage(new Message($e->getMessage(), 'tags'));
        return false;
    }
}

并将测试代码更改为:

$tags = array();
foreach(explode(',', $tagsText) as $tagText) {
    $tags[] = Tag::getOrCreate(trim($tagText));
}
$content->tags = $tags;