2016-07-22 9 views
1

몇 년 전 CakePHP는 2.2 뭔가를 개발했다. payed_beihilfe이 부분은 더 이상하지 않는 한 항상 0 인 상황이 발생한 때까지가상 필드

scheduled_payment = 1 
&& (payed_beihilfe IS NOT NULL && payed_beihilfe > 0) 
&& (payed_pkv IS NOT NULL && payed_pkv > 0) 

이 잘 근무하고있다 : 내 Bill 모델에서 나는 주어진 법안은 이미 "완료"여부를 계산하는 virtualField이 역할. 분포에 대한 정보는 0에서 100까지의 범위 인 Person.beihilfe_part 필드에 있습니다. 극단 중 하나 인 경우 해당 값을 고려하지 않습니다. 그래서 나는 이것을 처음 시도했습니다 :

scheduled_payment = 1 
&& ((payed_beihilfe IS NOT NULL && payed_beihilfe > 0) || Person.beihilfe_part = 0) 
&& ((payed_pkv IS NOT NULL && payed_pkv > 0) || Person.beihilfe_part = 100) 

이것은 하나의 Bill 개체를 볼 때 작동합니다. 그러나 Person을보고하자마자 are mentioned in the manual이라는 SQL 오류가 발생합니다. 기본적으로 이는 작업이 필요한 virtualFields을 가질 수 없다고 말합니다. 내가 가지고있는이 "완료된"속성을 실제로 확장해야하기 때문에 이것은 어리석은 짓입니다.

이 내 Bill 클래스 :

class Bill extends AppModel { 
    public $displayField = 'amount'; 

    public $virtualFields = array(
      # XXX There is a limitation in the `virtualFields` that it cannot 
      # use any sort of `JOIN`. Therefore I have to make due with the 
      # data that is available in the `bills` table. This is a real 
      # kludge but it seems to work. 
      "done" => 
       "scheduled_payment = 1 
       && ((payed_beihilfe IS NOT NULL && payed_beihilfe > 0) || (person_id = 7 && date > '2016-06-01')) 
       && (payed_pkv IS NOT NULL && payed_pkv > 0)" 
    ); 

    public $validate = array(
     'amount' => array(
      "rule" => array("comparison", ">", 0), 
      "message" => "Der Rechnungsbetrag muss größer als € 0 sein." 
     ) 
    ); 


    public $belongsTo = array(
     'Doctor' => array(
      'className' => 'Doctor', 
      'foreignKey' => 'doctor_id', 
      'conditions' => '', 
      'fields' => '', 
      'order' => '' 
     ), 
     'Person' => array(
      'className' => 'Person', 
      'foreignKey' => 'person_id', 
      'conditions' => '', 
      'fields' => '', 
      'order' => '' 
     ) 
    ); 
} 

이는 Person 모델 :

질의가 PeopleController::view 기능에 예를 들어 발생
class Person extends AppModel { 
    public $displayField = 'name'; 

    public $hasMany = array(
     'Bill' => array(
      'className' => 'Bill', 
      'foreignKey' => 'person_id', 
      'dependent' => false, 
      'conditions' => '', 
      'fields' => '', 
      'order' => '', 
      'limit' => '', 
      'offset' => '', 
      'exclusive' => '', 
      'finderQuery' => '', 
      'counterQuery' => '' 
     ) 
    ); 
} 

:

public function view($id = null) { 
    $this->Person->id = $id; 
    if (!$this->Person->exists()) { 
     throw new NotFoundException(__('Invalid person')); 
    } 
    $this->set('person', $this->Person->read(null, $id)); 
} 

이는 것 관련을 그려라.목적 및 Person 표 주어진 Person 관련된 Bill 오브젝트를 취득 쿼리에 JOIN ED가 아니므로 그것은 충돌. 이것은 CakePHP 내에서 암묵적으로 발생합니다. 저는 방금 스캐 폴딩을 사용했습니다.

내가 주로 웹 개발에서 멀리 이동 한 나는 꽤 동안 CakePHP는 함께 일하지 않은

. 아주 작은 Django 프로젝트에서 SQL 테이블의 행이 실제 파이썬 객체로 인스턴스화되는 것을 보았습니다. 함수 나 필드를 추가하는 것은 정말 쉽습니다. CakePHP에서는 모든 것이 연관 배열에 저장됩니다. 따라서이 "완료된"속성을 계산하는 복잡한 함수를 추가하는 방법을 실제로 알지 못합니다.

이것에 대해 갈 수있는 현명한 방법은 무엇입니까?

+0

보기? 그리고'Bill' 모델에 정확히 어떻게 쿼리하고 있습니까? – ndm

+0

귀하의 질문에 답변해야 할 코드를 추가했습니다. 지폐와 사람 사이에 일대일 관계가 있습니다. 'Person-> read'를 사용할 때 CakePHP가 자동으로 쿼리합니다. –

답변

2

불행하게도 이러한 한계를 극복 할 지나치게 깨끗한 방법이 없습니다. 하나의 쿼리를 수정할 수 협회, 대한 Model.beforeFind 이벤트 및 관련 데이터에 대한 쿼리가 내장 된 지점이 없습니다 완전히 데이터 소스 로직 (DboSource::generateAssociationQuery())의 일부를 재 구현없이 후크 불가능하다.

나는 이벤트를 전달하는 오버라이드 buildStatement() 메서드가있는 사용자 지정 데이터 소스를 사용하여 비슷한 문제를 해결했습니다. 이 이벤트는 모든 쿼리에 대해 디스패치되므로 연결된 모델은이를 수신하고 필요할 경우 쿼리를 수정할 수 있습니다. 이 같은

뭔가 :

응용 프로그램/모델/데이터 소스/데이터베이스/AppMysql.가상 필드 그들에 액세스 할 수 있도록 PHP

<?php 
App::uses('CakeEvent', 'Event'); 
App::uses('Mysql', 'Model/Datasource/Database'); 

class AppMysql extends Mysql 
{ 
    public function buildStatement($query, Model $Model) 
    { 
     $targetModel = $Model; 
     $linkedModel = $Model->{$query['alias']}; 
     if ($linkedModel !== null) { 
      $targetModel = $linkedModel; 
     } 

     $event = new CakeEvent('Model.beforeBuildStatement', $this, array($query, $Model)); 
     $targetModel->getEventManager()->dispatch($event); 

     if (is_array($event->result)) { 
      $query = $event->result; 
     } 

     return parent::buildStatement($query, $Model); 
    } 
} 

그런 다음 모델에 하나 추가 할 수 있습니다 조인. $Model는 현재 모델이 아닌 경우 귀하의 경우는

class Bill extends AppModel 
{ 
    // ... 

    public function implementedEvents() 
    { 
     return parent::implementedEvents() + array(
      'Model.beforeBuildStatement' => array(
       'callable' => 'beforeBuildStatement', 
       'passParams' => true 
      ) 
     ); 
    } 

    public function beforeBuildStatement(array $query, Model $Model) 
    { 
     if ($Model->name === 'Person') { 
      $query['joins'][] = array(
       'table' => 'persons', 
       'alias' => 'Person', 
       'type' => 'LEFT', 
       'conditions' => array(
        'Person.id = Bill.person_id', 
       ) 
      ); 
     } 

     return $query; 
    } 
} 

의 라인을 따라 뭔가 될 수있다, 이것은 현재의 모델은 협회 쿼리의 일환으로 조회되는 것을 나타냅니다. 이 스 니펫은 Person 모델에 대한 연결 쿼리의 일부로 bill 테이블이 쿼리 될 때마다 persons 테이블에 조인됩니다.

이 예제는 "parent model = Person model"인지 여부 만 유일한 문제이므로 가능한 함정이 있다는 점에 유의해야합니다. 앱에서 진행중인 작업에 따라 중복/비 고유 조인과 같은 문제를 피하기 위해 추가 조치를 구현해야 할 수도 있습니다.

또한`Person` 모델은`Bill` 모델에 관련이 정확히 어떻게