Данные CakePHP HABTM не сохраняются в базе данных

У меня есть модель GenForm, которая имеет связь HABTM с другой моделью PdfFile. Я использую это для создания списка флажков в моем индексном представлении GenForm. В модели GenForm я добавил:

public $hasAndBelongsToMany = array(
    'PdfFile' => array(
        'className' => 'PdfFile',
        'joinTable' => 'gen_forms_x_pdf_files'
    )

Вот фрагмент моего GenForm index.ctp представления:

<?php 
echo $this->Form->input( 'PdfFile', array('label' => 'Select some PDF files', 'multiple' => 'checkbox') );
echo $this->Form->input( 'first_name' );
echo $this->Form->input( 'last_name' );
?>

В контроллере у меня базовое сохранение:

    if ($this->request->is('post')) { // form was submitted
        $this->GenForm->create();
        if ($this->GenForm->save($this->request->data)) {
            return $this->redirect(array('action' => 'generate', $this->GenForm->id)); // assemble the PDF for this record
        } else {
            $this->Session->setFlash(__('Log entry not saved.'));
        }
    }

Теперь $this->data выглядит примерно так, когда я debug() это:

array(
    'PdfFile' => array(
        'PdfFile' => array(
            (int) 0 => '1',
            (int) 1 => '5'
        )
    ),
    'GenForm' => array(
        'first_name' => 'xxx',
        'last_name' => 'xxx',
        'association_id' => '1',
        'email' => ''
    )
)

Все работает отлично, но я не смог проверить флажки (должен быть установлен хотя бы один). Итак, согласно этому ответу, я внес некоторые изменения.

Вид index.ctp стал таким:

<?php 
echo $this->Form->input( 'GenForm.PdfFile', array('label' => 'Select some PDF files', 'multiple' => 'checkbox') );
echo $this->Form->input( 'first_name' );
echo $this->Form->input( 'last_name' );
?>

Вот мое правило проверки:

public $validate = array(
    'PdfFile' => array(
        'rule' => array(
            'multiple', array('min' => 1)
        ), 
        'message' => 'Please select one or more PDFs'
    )
)

Вот так сейчас выглядит $this->data:

array(
    'GenForm' => array(
        'PdfFile' => array(
            (int) 0 => '1',
            (int) 1 => '5'
        ),
        'first_name' => 'xxx',
        'last_name' => 'xxx',
        'association_id' => '1',
        'email' => ''
    )
)

Теперь флажки для PdfFile подтверждаются, но данные PdfFile не сохраняются, хотя другие поля для GenForm правильно сохраняются в своей собственной таблице.

Может ли кто-нибудь сказать мне, что мне не хватает, чтобы PdfFile сохранялось автоматически и проверялось?


person Blazemonger    schedule 26.08.2013    source источник


Ответы (2)


Первая форма правильная

Утверждая очевидное, но форма, которая сработала, - это форма для использования, т.е.:

echo $this->Form->input('PdfFile', array(
    'label' => 'Select some PDF files', 
    'multiple' => 'checkbox'
));

Изменение формы для поля с именем «PdfFile» просто не будет работать, так как слой модели удалит любые данные для полей, которые не существуют, и в этой форме он проверит наличие gen_forms.PdfFile, найдет, что поля нет, и проигнорирует данные.

Проверка

Чтобы позаботиться об ошибках проверки, используйте правило проверки, работающее в модели, которое проверяет количество сохраняемых записей habtm. Неважно, как называется поле, используемое для проверки, например:

<?php
class GenForm extends AppModel {

    public $validate = array(
        'dummy' => array( // <- name this whatever you like
            'atLeastOne' => array(
                'required' => true, // run always
                'rule' => array('validateAtLeastOne')
            )
        )
    );

    function validateAtLeastOne() {
        if (!isset($this->data['PdfFile'])) {
            // there is no pdf data at all, ignore this rule
            // allow other save operations to work
            return true;
        }

        $return = count(array_filter($this->data['PdfFile']['PdfFile']));
        if (!$return) {
            $this->PdfFile->invalidate('PdfFile', 'Please upload a file');
        }
        return $return;
    }

}

Поскольку правило проверки возвращает false, если записей нет, сохранение будет остановлено. При вызове invalidate для ассоциации HABTM с тем же именем поля, которое будет искать помощник формы, будет отображено сообщение об ошибке.

Альтернативно

Вы можете использовать второй подход в вопросе:

echo $this->Form->input('GenForm.PdfFile', array(
    'label' => 'Select some PDF files', 
    'multiple' => 'checkbox'
));

Полностью осознавая, что это не то, как Cake ожидает получить данные, а затем манипулировать ими, чтобы они были в правильном формате в beforeValidate:

<?php
class GenForm extends AppModel {

    public $validate = array(
        'PdfFile' => array( // existing rule
            ...
        )
    );

    function beforeValidate() {
        if (isset($this->data[$this->alias]['PdfFile'])) {
            // keep the existing data as that's what validation will check against
            // copy to the right location so Cake will process it
            $this->data['PdfFile']['PdfFile'] = $this->data[$this->alias]['PdfFile'];
        }
        return true;
    }

    ...
}
person AD7six    schedule 26.08.2013
comment
Первоначально у меня было правило проверки, основанное на этом ответе, но оно показалось мне неуклюжим и неэлегантным. - person Blazemonger; 26.08.2013
comment
k - тогда выбирай между работами но тупо и вообще не работает =) - person AD7six; 26.08.2013
comment
Но разве не должен быть способ заставить PdfFile сохраниться на основе второй договоренности? - person Blazemonger; 26.08.2013
comment
совсем просто нет. вторая схема не определяет отношения habtm — она определяет поле в GenForm с именем PdfFile, которое не существует и, следовательно, ничего не делает. Тем не менее, если вы предпочитаете, вы можете использовать это и использовать beforeValidate для копирования GenForm.PdfFile в PdfFile.PdfFile, чтобы он работал. - person AD7six; 26.08.2013
comment
Я подумал, что, возможно, смогу извлечь PdfFile из массива GenForm в $this->data и вручную передать его в $this->GenForm->PdfFile->save(), но это было столь же неуклюже и неэлегантно. - person Blazemonger; 26.08.2013
comment
Добавление правил проверки к моему объекту PdfFile тоже не сработало — они были помечены как обязательные в представлении, но фактическая проверка не запускалась во время сохранения. - person Blazemonger; 26.08.2013
comment
Я не сказал добавить проверку к вашей модели PdfFile =) (это ничего не даст - вы не сохраняете записи PdfFile - если только я полностью не понял вопрос). Я обновил ответ перестановкой второго подхода, который работает. - person AD7six; 26.08.2013
comment
Последний вопрос: можете ли вы объяснить эту цитату из документации? validates() — public — возвращает true, если все поля прошли проверку. Будет проверять ассоциации hasAndBelongsToMany, которые также используют ключ with. Поскольку _saveMulti не может выйти из операции сохранения. - person Blazemonger; 26.08.2013
comment
@Blazemonger, если вы используете with, это означает, что существует специальная модель присоединения; например Пользователи ‹- Членство (habtm, таблица имеет больше первичного ключа и больше 2-х полей) -> Команда. В нем говорится, что он проверит модель членства - это не имеет отношения к вашему первоначальному вопросу =). - person AD7six; 27.08.2013
comment
Хорошо -- думаю, я доволен тем, как это работает сейчас. Возможно, когда я пойму, что делаю, я предложу это для улучшения кодовой базы. - person Blazemonger; 27.08.2013

Если я правильно помню, из руководства по тортам, вероятно, вы неправильное форматирование данных.

Попробуйте, чтобы $this->data выглядело так:

array(
    'GenForm' => array(
        'first_name' => 'xxx',
        'last_name' => 'xxx',
        'association_id' => '1',
        'email' => ''
    ),
    'PdfFile' => array(
        (int) 0 => '1',
        (int) 1 => '5'
    ),
)
person Roberto Maldonado    schedule 26.08.2013