Подтвердить что ты не робот

CakePHP 2.x: Действительно ли полезен флаг $primary на Model:: afterFind()?

Обратный вызов CakePHP Model::afterFind() выглядит следующим образом:

afterFind(array $results, boolean $primary = false)

Согласно документации:

Параметр $primary указывает, была ли текущая модель моделью, на которой возник запрос, или была ли эта модель запрошена как ассоциация. Если модель запрашивается как ассоциация, формат $results может отличаться.

Они могут отличаться, но эксперименты показывают, что они не всегда различаются. Насколько я могу судить, параметр $primary на самом деле не является полезным. Если он установлен на false, вы можете или не можете получить сглаженную структуру данных, поэтому вы можете или не можете завершить сообщение об ошибке "не можете использовать строковое смещение как массив".

Хотя я еще не пробовал, моя мысль, основанная на документации, заключалась в том, чтобы игнорировать флаг $primary вообще и просто проверить данные:

public function afterFind($results, $primary = false) {
  if (array_key_exists(0, $results) {
    // operate on $results[0]['User']['fieldname']
  } else {
    // operate on $results['fieldname']
  }
  return $results;
}

Это хаки, и мне это не нравится, но, похоже, оно более полезно, чем $primary.

Явно заявляю, что мои вопросы:

  • Что такое флаг $primary, который действительно полезен для?
  • Правильно ли, что это не полезно для определения структуры массива $results или я что-то пропустил там?
4b9b3361

Ответ 1

Действительно, параметр $primary кажется полезным только для предупреждения о случаях непредсказуемости формата $results. Это не полезно при определении формата $results.

Дополнительная информация здесь: https://groups.google.com/forum/?fromgroups=#!topic/cake-php/Mqufi67UoFo

Предлагаемое решение состоит в проверке !isset($results[$this->primaryKey]), чтобы узнать, какой формат $results. Это также немного взломать, но, возможно, лучше, чем проверка ключа "0".

Решение, которое я в конечном итоге придумал, состоит в том, чтобы сделать что-то вроде этого:

public function afterFind($results, $useless) {

    // check for the primaryKey field
    if(!isset($results[$this->primaryKey])) {
        // standard format, use the array directly
        $resultsArray =& $results;
    } else {
        // stupid format, create a dummy array
        $resultsArray = array(array());
        // and push a reference to the single value into our array
        $resultsArray[0][$this->alias] =& $results;
    }

    // iterate through $resultsArray
    foreach($resultsArray as &$result) {
        // operate on $result[$this->alias]['fieldname']
        // one piece of code for both cases. yay!
    }

    // return $results in whichever format it came in
    // as but with the values modified by reference
    return parent::afterFind($results, $useless);
}

Это уменьшает дублирование кода, потому что вам не нужно дважды писать логику изменения поля (один раз для массива и один раз для не-массива).

Возможно, вам удастся избежать ссылок на материал, просто вернув $resultsArray в конце метода, но я не был уверен, какие проблемы могут возникнуть, если CakePHP (или какой-либо другой родительский класс) ожидает $results в том, как он был передан. Кроме того, этот способ не имеет накладных расходов на копирование массива $results.

Ответ 2

Если вы не можете всегда полагаться на primaryKey в списке полей, и вы знаете ключ, который вы ищете, вы можете уйти с чем-то более простым. Вот пример:

/**
 * Decrypt password
 *
 * @see Model::afterFind()
 */
public function afterFind($results, $primary = false) {        
    if (!empty($results['password'])) {
        $results['password'] = Security::rijndael($results['password'], Configure::read('encrypt.key'), 'decrypt');
        return $results;
    }

    foreach ($results as &$r) {
        if (!empty($r[$this->alias]['password'])) {
            $r[$this->alias]['password'] = Security::rijndael($r[$this->alias]['password'], Configure::read('encrypt.key'), 'decrypt');
        }
    }
    return $results;
}

Ответ 3

Я столкнулся с этой проблемой. Принятый ответ работает хорошо. Однако мне пришлось внести незначительную корректировку. Если вы хотите изменить поле, например, создать полное имя файла из логотипа, лучше создать новое поле, как "return parent:: afterFind ($ results, $бесполезно)"; будет делать это дважды, если поиск модели вызывается из какой-либо другой модели.

foreach($resultsArray as &$result) {
        // operate on $result[$this->alias]['fieldname']
        // one piece of code for both cases. yay!

        // Added logoFull instead of modifying logo
        if(isset($result[$this->alias]['logo'])){
            $result[$this->alias]['logoFull'] = Configure::read('urlImg') . 'logos' . DIRECTORY_SEPARATOR .
                'carrier' . DIRECTORY_SEPARATOR . $result[$this->alias]['logo'];
        }
    }

Ответ 4

Ответы в книге...

Параметр $primary указывает, была ли текущая модель модель, на которую возник запрос, или эта модель был запрошен как ассоциация. Если модель запрашивается как ассоциация формат $results может отличаться;

Код, ожидающий, что $primary будет правдой, вероятно, получит "Невозможно использовать строковое смещение как массив" фатальная ошибка PHP, если рекурсивная находка б.

Таким образом, это может быть полезно в определенных ситуациях для логической обработки и может быть использовано для того, чтобы иметь эффект стука в ваши результаты $