Nasıl yuva CakePHP ile katıldı?

2 Cevap php

Ben davranmaya çalışıyorum. Yani, yerine şu SQL sözdizimi kullanarak:

select *
from   tableA INNER JOIN
       tableB on tableA.id = tableB.tableA_id LEFT OUTER JOIN
       ( tableC INNER JOIN tableD on tableC.tableD_id = tableD.id)
       on tableC.tableA_id = tableA.id

Ben CakePHP'ye model->find() kullanmak istiyorum. Bu, bu kadar anladığım kadarıyla özel SQL sorguları ile çalışmak olmaz çünkü (bana biraz esnek görünüyor modele tek Sayfalandırması sorgu kodlamalısınız sürece) ben de Paginator kullanmak izin verir.

Ben bugüne kadar ne denedim:

/* inside tableA_controller.php, inside an action, e.g. "view" */
$this->paginate['recursive'] = -1; # suppress model associations for now
$this->paginate['joins'] = array(
    array(
        'table' => 'tableB',
        'alias' => 'TableB',
        'type'  => 'inner',
        'conditions' => 'TableB.tableA_id = TableA.id',
    ),
    array(
        'table' => 'tableC',
        'alias' => 'TableC',
        'type'  => 'left',
        'conditions' => 'TableC.tableA_id = TableA.id',
        'joins' = array( # this would be the obvious way to do it, but doesn't work
            array(
                'table' => 'tableD',
                'alias' => 'TableD',
                'type'  => 'inner',
                'conditions' => 'TableC.tableD_id = TableD.id'
            )
        )
    )
)

Bu yapının içine katılır iç içe olduğunu. Ama bu (CakePHP sadece beklediğim ne tür iç içe 'joins' eleman yok sayar, ama üzgün çalışmıyor.

Ben bir deyim oluşturucu kullanarak (where fıkrasında) alt sorgular nasıl yorumlarına ipuçlarını gördük. Benzer bir hile burada kullanılabilir mi?

2 Cevap

Bu yapamazsın çıkıyor. En azından CakePHP 1.2.6 yukarıda değil sağlanan sözdizimi ile. Ben kaynağına gitti (yay! kaynak çerçeveler açık!) Ve katılır için kod içeren cake/libs/model/datasources/dbo_source.php dosya bulundu.

Tüm DboSource::renderStatement() ki bazı kadar toplama yapar ki, DboSource::buildJoinStatement($join) üzerinden SQL fragmanları ile bu tanımları katılmak yerine, $query['joins'] dizisinin sığ bir yürüyüş yapar ile başlar sonra argümanlar (boşlukları vb doldurma) ve DboSource::renderJoinStatement, tek bir katılmak fıkra SQL fragmanı yaratmak için çağırır.

me: Bu düzeltmek için kolay olmalı!

I cake/libs içinde şeyler düzenlemek için değil söylendi, bu yüzden yerine ben dosyayı dbo_source.php için app/models/datasources/ düzenleme için kopyalandı. Sonra benim baltayı aldı ve yeni bir yöntem haline DboSource::renderStatement() olarak $query['joins'] dizisinin sığ yürüyüş refactored DboSource::buildJoinStatementArray() bu iki yöntem sonucu:

function buildStatement($query, $model) {
    $query = array_merge(array('offset' => null, 'joins' => array()), $query);

    # refactored (extract method) to make recursion easier
    $query['joins'] = $this->buildJoinStatementArray($query['joins']);

    return $this->renderStatement('select', array(
        'conditions' => $this->conditions($query['conditions'], true, true, $model),
        'fields' => implode(', ', $query['fields']),
        'table' => $query['table'],
        'alias' => $this->alias . $this->name($query['alias']),
        'order' => $this->order($query['order']),
        'limit' => $this->limit($query['limit'], $query['offset']),
        'joins' => implode(' ', $query['joins']),
        'group' => $this->group($query['group'])
    ));
}
/**
 * Replaces the join statement array syntax with SQL join clauses.
 */
function buildJoinStatementArray($joins) {
    if (!empty($joins)) {
        $count = count($joins);
        for ($i = 0; $i < $count; $i++) {
            if (is_array($joins[$i])) {
                $joins[$i] = $this->buildJoinStatement($joins[$i]); # $joins[$i] now contains something like "LEFT JOIN users As User on User.group_id = Group.id"
            }
        }
    }
    return $joins;
}

Ben bir kez DboSource::buildJoinStatementArray(), bunu değiştirmek için DboSource::buildJoinStatement() zaman oldu - bütün I $data['joins'] ve bu durum için bir alternatif oluşturma yöntemi için bir çek eklendi etmedi:

function buildJoinStatement($join) {
    $data = array_merge(array(
        'type' => null,
        'alias' => null,
        'table' => 'join_table',
        'conditions' => array()
    ), $join);

    if (!empty($data['alias'])) {
        $data['alias'] = $this->alias . $this->name($data['alias']);
    }
    if (!empty($data['conditions'])) {
        $data['conditions'] = trim($this->conditions($data['conditions'], true, false));
    }

    # allow for nested joins
    if (!empty($data['joins']) and is_array($data['joins'])) {
        $data['joins'] = $this->buildJoinStatementArray($data['joins']);
        return $this->renderNestedJoinStatement($data);
    }
    else
    {
        return $this->renderJoinStatement($data);
    }
}

Yeni renderNestedJoinStatement() yöntemi oldukça benzer DboSource::renderJoinStatement():

/**
 * Renders a final SQL JOIN that contains nested join statements
 *
 * @param array $data
 * @return string
 */
function renderNestedJoinStatement($data) {
    extract($data);
    $nestedJoins = implode(' ', $joins);
    return trim("{$type} JOIN ({$table} {$alias} {$nestedJoins})ON ({$conditions})");
}

Ben bu hakkı alıyorum varsa, (umarım modellerde) aşağıdaki ilişkileri var:

TableA hasMany TableB.
TableA hasMany TableC.

TableB belongsTo TableA.

TableC belongsTo TableA.
TableC belongsTo TableD. (might be hasOne)

TableD hasMany TableC. (might be hasOne)

(Ben çok tavsiye ve miras tüm modeller için app_model düzeyde ayarlamak) Containable davranışı kullanarak ediyorsanız, ben böyle bir şey yapabileceğimizi düşünüyorum ...

$this->TableA->find(
  'all',
  array(
    'contain' => array(
      'TableB',
      'TableC' => array(
        'TableD'
      )
    ),
    'conditions' => array(...),
    'order' => array(...)
  )
);

Belirli alanları almak gerekiyorsa, o zaman mesela ben burada TABLEB adlı döndü alanlarını kısıtlamak, içeren parametre bunları belirtmek gerekir:

$this->TableA->find(
  'all',
  array(
    'contain' => array(
      'TableB' => array(
        'fields' => array(
          'field_1',
          'field_2'
        ),
      ),
      'TableC' => array(
        'TableD'
      )
    ),
    'conditions' => array(...),
    'order' => array(...)
  )
);

Döndürülen veriler şöyle olmalıdır:

  [0] => array(
    [TableA] => array(
      [id] => 12,
      [name] => 'Foo'
    ),
    [TableB] => array(
      [id] => 23,
      [table_a_id] => 12,
      [name] => 'Bah'
    ),
    [TableC] => array(
      [id] => 45,
      [table_a_id] => 12,
      [table_d_id] => 67,
      [name] => 'Woo',
      [TableD] => array(
        [0] => array(
          [id] => 67,
          [table_a_id] => 12,
          [name] => 'Wah'
        )
      )
    )
  )

Iç içe geçmiş tablo kap (masaya ve TableC) üstüdür, bu nedenle işe yaramayabilir nerede Ancak, ben hiç yapmamıştım, ama muhtemelen bir cami bulunuyor.