繁体   English   中英

CakePHP 3.x:使用文本输入添加BelongsTo实体

[英]CakePHP 3.x: Add BelongsTo entity by using text input

这个问题对我来说似乎很基础,所以我想知道它出了什么问题。

我将通过一个简单的例子来展示我的问题。 我用一个简单的belongsTo关系创建了两个表。

create table philosophers (
    id int unsigned primary key auto_increment,
    full_name varchar(255) not null unique
);
create table books (
    id int unsigned primary key auto_increment,
    title varchar(255) unique not null,
    philosopher_id int unsigned not null,
    foreign key `philosopher_id` (philosopher_id) references philosophers(id)
)

并在新鲜的cakePHP 3.4.8安装上烘烤所有东西。 到现在为止还挺好。 这是一个问题:

我想在一个文本框中写下Philosopher的名字,并让CakePHP将它与现有名称相关联(如果它已存在),或者添加一个新名称(如果它尚不存在)。 所以,根据惯例,我更换

echo $this->Form->control('philosopher_id', ['options' => $philosophers]);

在文件src / Template / Books / add.ctp中 ,用:

echo $this->Form->control('philosopher.full_name');

在第二种情况下(添加一个新条目),它可以很好地工作,添加外键和所有。

为了实现第一个选择,我试过了

  • $entity->save()阶段为关联表隐式设置'checkExisting'
  • 使得在Philosopher实体中可以访问id
  • 创建一个在beforeMarshal事件中添加id的行为。

这是行为:

见下文

它似乎并不想创建现有实体。 我知道我可以做到这里所说的,但这实际上完全绕过了验证。

我几乎可以肯定我错过了一些东西......希望我知道它是什么。

编辑:我更新并纠正了行为,考虑了@ndm的解决方案。

namespace App\Model\Behavior;

use Cake\ORM\Behavior;
use Cake\Event\Event;
use ArrayObject;
use Cake\ORM\TableRegistry;
use Cake\Utility\Inflector;

/**
 * This class prevents the belongsTo relation from
 * always creating new entries, by modifying the data
 * before it is marshalled.
 *
 * The config should have an entry called 'fields':
 *
 *  - 'fields' An array of field names, formatted
 *             according to cakePHP conventions
 *             for BelongsTo associations.
 */
class MarshalAssocBehavior extends Behavior {

    protected $_defaultConfig = [
        'fields' => []
    ];

    public function beforeMarshal (Event $event,
                                   ArrayObject $data,
                                   ArrayObject $options) {
        $fields = $this->getConfig('fields');
        foreach ($fields as $field) {
            $temp = explode('.', $field);
            $fd_name = $temp[0];
            $column = $temp[1];
            unset($temp);
            /*
             * If @$data does not contain required keys,
             * skip and evaluate next config block.
             */
            if (   !array_key_exists($fd_name, $data)
                || !array_key_exists($column, $data[$fd_name])
            ) continue;

            $table_name = Inflector::pluralize(Inflector::camelize($fd_name));
            $table = TableRegistry::get($table_name);

            /**
             * @var Cake\Datasource\EntityInterface $result
             */
            $result = $table->find()
                            // value (user-provided) is escaped by Cake
                            ->where([$column => $data[$fd_name][$column]])
                            ->first();
            if ($result) {
                unset($data[$fd_name]);
                $data[$fd_name.'_id'] = $result->id;
            }
        }
    }
}

要将它合并到我的BooksController

public function add() {
    $this->Books
         ->addBehavior('MarshalAssoc', [
                 'fields' => ['philosopher.full_name']);

使用beforeMarshal地修改数据是beforeMarshal的方法,但是你需要填充书籍数据中的外键,即设置philosopher_id ,并删除philosopher

unset($data['philosopher']);
$data['philosopher_id'] = $result->id;

philosopher.id仅在更新现有记录时使用。

此外TableRegistry::exists()可能会摆脱困境,即使你不会指望它,因为有可能没有实例尚未设置!

最后但并非最不重要的是, where($data[$field])是危险的,这是一个可能的SQL注入漏洞,因为传递的数组的关键手边将按原样插入到查询中(整个值也可能是字符串,也将按原样插入),并且可能由用户定义。 如果要使用此类可配置/可重用/动态功能,则应为字段名实现白名单,并自行构建条件数组。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM