この問題はかなり基本的なもので、どこが間違っているのか知りたいと思います。CakePHP 3.x:テキスト入力を使用してBelongsToエンティティを追加してください
簡単な例で私の問題を示します。私は単純なbelongsToの関係を持つ2つのテーブルを作成します。
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インストールですべてを焼く。 これまでのところとても良いです。
私は哲学者の名前をテキストボックスに書いて、既存の名前があればそれを関連付けるか、まだ存在しない場合は新しい名前を付けます。だから、規則に従って、私は、のsrc /テンプレート/書籍/ add.ctpファイルに
echo $this->Form->control('philosopher_id', ['options' => $philosophers]);
を置き換えます(新しいエントリを追加すること)は、第2のケースで
echo $this->Form->control('philosopher.full_name');
、それは外的なキーとすべてを加えて、すばらしく動作します。
最初のオプションを達成するために、私は暗黙$entity->save()
相に関連するテーブルの'checkExisting'
を設定
- を試みました。
- エンティティで
id
にアクセスできるようにする。 beforeMarshal
イベントでIDを追加する動作を作成します。
これは行動です:
はそれだけで既存のエンティティを作成したいとは思われない
下記を参照してください。 私はそれがhereと言うことをすることができることを知っていますが、これは事実上完全に検証をバイパスします。
私は何かが不足していることをほとんど確信しています...私はそれが何であるかを知っていれば幸いです。
EDIT:@ndmの解決策を考慮して、動作を更新して修正しました。私BooksController
にそれを組み込むこと
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;
}
}
}
}
:それに応じてデータを変更するbeforeMarshal
を使用し
public function add() {
$this->Books
->addBehavior('MarshalAssoc', [
'fields' => ['philosopher.full_name']);
まあ、私は本からメソッドを試しました(エンティティを取得して手動で設定する)、失敗します。 あなたの提案が唯一の方法だと思われます。あなたはまた正しい:SQLインジェクションです。私はこれに気づいたと思っていますが、私はこの問題から非常に不満です。それは、しかし、ポイントを超えています。 cakePHPの扱い方は実用的だと思いますか?私はそれがむしろ混乱していると感じます。どうもありがとうございました! –
また、 'checkExisting'がエンティティが既存のエントリに対応するかどうかを実際にチェックしない場合、どうしますか? –
"_being practical_"と正確に何を参照しているのかわかりませんか? 'checkExisting'は、レコードが存在するかどうかをチェックしますが、新しい(' EntityInterface :: isNew() ')としてマークされ、プライマリキーが設定されているエンティティに対してのみ、これはマーシャリング後にも起こります。エンティティに少なくとも1つのダーティ/変更されたプロパティがある場合...そのオプションに依存しているコンテキストが不明です。 @NobbyNobbs – ndm