2017-06-22 6 views
1

この問題はかなり基本的なもので、どこが間違っているのか知りたいと思います。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']); 

答えて

0

が移動するための方法である、しかし、あなたは代わりに書籍データの外部キーを移入する必要があるだろう、すなわちphilosopher_id設定、およびphilosopherを削除します。

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

philosopher.idを既存のレコードを更新する場合にのみ使用されます。

TableRegistry::exists()インスタンスが設定されていない可能性があるため、期待していなくてもベールアウトする可能性があります。まだ

最後に、where($data[$field])は危険です。渡された配列のキー側がそのままクエリに挿入されるため、SQL injectionの脆弱性が存在します(全体の値は文字列でもかまいません)。同様に挿入される)、潜在的にユーザによって定義されることができる。このような構成可能/再利用可能/動的機能を使用する場合は、フィールド名のホワイトリストを実装し、条件配列を自分で作成する必要があります。

+0

まあ、私は本からメソッドを試しました(エンティティを取得して手動で設定する)、失敗します。 あなたの提案が唯一の方法だと思われます。あなたはまた正しい:SQLインジェクションです。私はこれに気づいたと思っていますが、私はこの問題から非常に不満です。それは、しかし、ポイントを超えています。 cakePHPの扱い方は実用的だと思いますか?私はそれがむしろ混乱していると感じます。どうもありがとうございました! –

+0

また、 'checkExisting'がエンティティが既存のエントリに対応するかどうかを実際にチェックしない場合、どうしますか? –

+0

"_being practical_"と正確に何を参照しているのかわかりませんか? 'checkExisting'は、レコードが存在するかどうかをチェックしますが、新しい(' EntityInterface :: isNew() ')としてマークされ、プライマリキーが設定されているエンティティに対してのみ、これはマーシャリング後にも起こります。エンティティに少なくとも1つのダーティ/変更されたプロパティがある場合...そのオプションに依存しているコンテキストが不明です。 @NobbyNobbs – ndm

関連する問題