Как использовать делегирование класса Propel

Я использую Propel 1.7-dev и играю с делегированием классов, т. Е. С таблицей "наследование". У меня Propel1 и Phing настроены как подмодуль Git, и каждый проверен на своих соответствующих мастерах. Мои модели строятся, и я могу заставить иерархию работать, но если я делаю это согласно документам (возможно, более чисто), то это терпит неудачу.

Вот соответствующий фрагмент моих моделей:

<table name="process_step">
    <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
    <column name="process_id" type="integer" required="true" />
    <column name="ordinal" type="integer" required="true" />
    <column name="comment" type="varchar" size="256" />
    <column name="type" type="enum" required="true"
        valueSet="load, submit_form, check_page, get_links"
    />
    <column name="is_enabled" type="boolean" required="true" default="false" />

    <foreign-key foreignTable="process">
        <reference local="process_id" foreign="id" />
    </foreign-key>
</table>

<table name="process_step_load">
    <column name="id" type="integer" required="true" primaryKey="true" />
    <behavior name="delegate">
        <parameter name="to" value="process_step" />
    </behavior>
    <column name="url" type="varchar" size="1024" />
    <column name="method" type="varchar" size="6" required="true" default="get" />
</table>

На практике существует гораздо больше дочерних моделей, но я для краткости убрал их.

Первое, на что нужно обратить внимание, это то, что в документах показаны определения родительского и дочернего классов, которые имеют автоматически увеличиваемые первичные ключи. Это, однако, невозможно: первичный ключ должен быть установлен в родительском и скопирован в дочерний (так как родительский всегда будет иметь строку). Я считаю, что это ошибка в документации, но я предложу патч для этого после этого вопроса, на случай, если я что-то неправильно понял.

Итак, вот мой рабочий код:

    $process = new Process();

    $processStep = new ProcessStep();
    $processStep->setOrdinal(1); // Simplified for brevitiy
    $processStep->setType(1); // Ditto
    $processStep->setProcess($process);

    // This creates a primary key which we use below (i.e. a cascading
    // save won't work)
    $processStep->save();

    $processStepChild->setId($processStep->getId());
    $processStepChild->save();

Вот как документы будут иметь:

    $process = new Process();

    $processStep = new ProcessStep();
    $processStep->setOrdinal(1); // Simplified for brevitiy
    $processStep->setType(1); // Ditto
    $processStep->setProcess($process);

    $processStepChild->setProcessStep($processStep);
    $processStepChild->save();

Результатом этого является то, что, когда я создаю четыре дочерние строки (каждая разного типа), первичные ключи отлично устанавливаются в родительском (ProcessStep), но все равны нулю в дочерних (ProcessStepLoad и другие).

Даже если я добавлю промежуточное сохранение, это не поможет. Таким образом, мне кажется, что $processStepChild->setProcessStep($processStep) проблема в том, что он должен работать с дочерним делегатом и изменить первичный ключ этого дочернего элемента на ключ предоставленного родительского элемента. Это явно не так.

Некоторые вопросы, которые могут быть актуальны:

  • Правильно ли я понял делегирование классов? Насколько я могу судить, родительский ключ должен представлять собой первичный ключ с автоинкрементом, и его необходимо использовать в дочернем как первичный ключ вручную для формирования иерархических отношений. (Помимо: я бы подумал, что будет способ автоматически определить тип от родителя, но, учитывая, что он не добавляет сам столбец типа, это невозможно сделать. Вот почему я добавил enum в родительском вручную)
  • Могло ли мое использование Propel master, а не формального релиза 1.6.x, что-то сломать?

1 ответ

Решение

Правильно, я только что переключился на движок Myno InnoDB, чтобы я мог использовать ограничения, и моя новая родительская таблица выглядит следующим образом. Это показывает, где что-то идет не так; отношения на столах неправильные:

CREATE TABLE `process_step`
(
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `process_id` INTEGER NOT NULL,
    `ordinal` INTEGER NOT NULL,
    `comment` VARCHAR(256),
    `type` TINYINT NOT NULL,
    `is_enabled` TINYINT(1) DEFAULT 0 NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `process_step_FI_1` (`process_id`),
    CONSTRAINT `process_step_FK_1`
        FOREIGN KEY (`process_id`)
        REFERENCES `process` (`id`),
    CONSTRAINT `process_step_FK_2`
        FOREIGN KEY (`id`)
        REFERENCES `process_step_load` (`id`)
        ON DELETE CASCADE,
    CONSTRAINT `process_step_FK_3`
        FOREIGN KEY (`id`)
        REFERENCES `process_step_submit_form` (`id`)
        ON DELETE CASCADE,
    CONSTRAINT `process_step_FK_4`
        FOREIGN KEY (`id`)
        REFERENCES `process_step_check_page` (`id`)
        ON DELETE CASCADE,
    CONSTRAINT `process_step_FK_5`
        FOREIGN KEY (`id`)
        REFERENCES `process_step_get_links` (`id`)
        ON DELETE CASCADE
) ENGINE=InnoDB;

Последние четыре (для дочерних таблиц), конечно, неверны: у нас не может быть внешнего ключа для каждого ребенка, так как это потребует от каждого ребенка расширения каждого родителя. Оказывается, это происходит при отсутствии явного ФК у ребенка. Таким образом, я изменил класс потомков в схеме, чтобы включить FK для родителя:

<table name="process_step_load">
    <column name="id" type="integer" required="true" primaryKey="true" />
    <foreign-key foreignTable="process_step">
        <reference local="id" foreign="id" />
    </foreign-key>
    <behavior name="delegate">
        <parameter name="to" value="process_step" />
    </behavior>
    <column name="url" type="varchar" size="1024" />
    <column name="method" type="varchar" size="6" required="true" default="get" />
</table>

Это правильно создает ограничения для всей базы данных:

DROP TABLE IF EXISTS `process_step`;

CREATE TABLE `process_step`
(
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `process_id` INTEGER NOT NULL,
    `ordinal` INTEGER NOT NULL,
    `comment` VARCHAR(256),
    `type` TINYINT NOT NULL,
    `is_enabled` TINYINT(1) DEFAULT 0 NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `process_step_FI_1` (`process_id`),
    CONSTRAINT `process_step_FK_1`
        FOREIGN KEY (`process_id`)
        REFERENCES `process` (`id`)
) ENGINE=InnoDB;

-- ---------------------------------------------------------------------
-- process_step_load
-- ---------------------------------------------------------------------

DROP TABLE IF EXISTS `process_step_load`;

CREATE TABLE `process_step_load`
(
    `id` INTEGER NOT NULL,
    `url` VARCHAR(1024),
    `method` VARCHAR(6) DEFAULT 'get' NOT NULL,
    PRIMARY KEY (`id`),
    CONSTRAINT `process_step_load_FK_1`
        FOREIGN KEY (`id`)
        REFERENCES `process_step` (`id`)
) ENGINE=InnoDB;

Это позволило мне использовать каскадное сохранение в соответствии с документами, ура!

Мораль истории: (а) я должен читать документы более тщательно, (б) документы иллюстрируют многие из них:1 наследование совершенно правильно, (в) с моими внешними ключами, наследование 1:1 также возможно в Propel.

Другие вопросы по тегам