Есть вопрос по наследованию и загрузке данных

SiZE

Новичок
Я работаю с AR в Yii. Одна модель одна таблица. Есть модель "шаблон", наследуется от базового класса, она содержит идентификатор, имя и тип шаблона. Так же есть отдельные модели: шаблон договора, шаблон счет, шаблон накладной и тп, наследуемые от базового класса. У этих моделей в связях указана модель "шаблон", они хранят ее идентификатор. Есть еще модели блоков аблонов.

При создании нового шаблона. Я обращаюсь к нужной модели, например, шаблон договора. Атрибуты этой модели заполняются значениями по умолчанию. Так же в атрибутах я храню объекты других моделей, в т.ч. модель "шаблон" и например модель "пункты договора", которая содержит динамически изменяемое количество пунктов, состоящих из заголовка и описания. При сохранении, я заполняю каждую модель данными и вызываю метод сохранения, если пройдена валидация.

И вот что меня мучает. Когда пользователь будет создавать документ и выбирать шаблон, который он хочет использовать, я буду хранить ссылку из таблицы "шаблон", а не "шаблон договора". Когда пользователь создаст документ, сохранит, у него появится возможность его распечатать.

Меня мучает вот такой вопрос, как правильно получить шаблон со всеми данными? Первое что приходит, передаем ИД "шаблона", получаем "тип". По типу пишем костыль if ( тип == 'шаблон договора') загружаем модель "шаблон договора" ну и дальше все понятно. Но это говно код какой-то получается. Я хотел бы чтобы документ автоматически собирал все данные и подтягивал форму для печати. Что поменять в структуре наследования?
 

hell0w0rd

Продвинутый новичок
Не очень понял что за шаблон, но скорее всего надо сразу по пользователю сохранять что за шаблон начальный он выбрал + сериализированные данные по тому как заполнить этот шаблон в итоге..
user | tamplate_type | data
 

SiZE

Новичок
hell0w0rd, дружище, ты вообще меня не понял =)

template (шаблон): id, name, template_type
contract (шаблон договора, связь один к одному): template_id, header, body, footer
block_clause (блок пункт договора, связь многие к одному): template_id, title, descroption
 

hell0w0rd

Продвинутый новичок
Так и в чем тогда сложность? Запись юзера должна указывать на id шаблона, а от туда уже и тип и все что угодно можно узнать
 

WMix

герр M:)ller
Партнер клуба
PHP:
 if ( тип == 'шаблон договора') загружаем модель "шаблон договора"
ну идея в принципе правильная, яб так написал
PHP:
 if ( class_exists('шаблон_договора') && is_subclass_of('шаблон_договора ', 'шаблон'))
is_subclass_of будет работать как whitelists и тут хоть пустой interface используй
 

SiZE

Новичок
PHP:
 if ( тип == 'шаблон договора') загружаем модель "шаблон договора"
ну идея в принципе правильная, яб так написал
PHP:
 if ( class_exists('шаблон_договора') && is_subclass_of('шаблон_договора ', 'шаблон'))
is_subclass_of будет работать как whitelists и тут хоть пустой interface используй
Я хотел избежать прописывания в базовом классе информации о дочерних.
 

Absinthe

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

SiZE

Новичок
Ну как?
PHP:
// Реалиации для Yii. Атрибуты модели являются столбцами таблицы.

// Модель таблицы template
class Template extends CActiveRecord {
  // ID
  public $id;
  // User ID
  public $user_id;
  // Template class name
  public $template_class;

  public static function model( $className=__CLASS__ ){
    return parent::model($className);
  }
}

// Модель таблицы template_contract
class TemplateContract CActiveRecord {
  // Template ID
  public $template_id;
  // Variable text field
  public $text;

  public static function model( $className=__CLASS__ ){
    return parent::model($className);
  }
}

// Фасад
class TemplateManager {
  public static function factory( $template_class=null ){
    if ( $template_class === null ) {
      return new TemplateManager;
    }
    return new $template_class.'Manager';
  }

  public function loadModel( $id ){
    // Попытка достать имя класса из БД
    $result = Template::model()->findByPk( $id );
    if ( !$result ) {
      return null
    }
    
    $classname = $result->template_class.'Manager';
    if ( !class_exists( $classname ) ) {
      return null;
    }
    if ( !is_subclass_of( $classname, 'TemplateManager ' ) ) {
      return null;
    }
    return new $classname;
  }
}

class TemplateContractManager extends TemplateManager {
  public function loadModel( $id ){
    // Тут идет загрузка нужной модели с данными
    $model = TemplateContract::model()->findByPk( $id );
    // тут какие-то действия по собиранию связанных данных этой модели
    return $model;
  }
}

class TemplateController {
  public function actionIndex( $id ){
    $model = TemplateManager::factory()->loadModel( $id );
  }
}
 

WMix

герр M:)ller
Партнер клуба
я немного сломал голову пытаясь разобраться.

это я непонял и очень важно для представления
Template это отдельная таблица? (или всеже это тип модели <<interface>> а возможно даже свойство traits)

правильно ли я понимаю, ты хочешь, имея фабрику TemplateManager, создавать обьекты CActiveRecord?

те. запись
PHP:
$model = TemplateManager::factory('Contract')->loadModel( $id );
должна вернуть строку с индификатором $id таблицы Contract, по типу CActiveRecord
при этом метод loadModel запрятан в специальном классе TemplateContractManager который (и только он) умеет заполнять ContractTemplate?

или другими словами в Controller известны имя таблицы и индификатор записи и имеется статичная фабрика которая вернет на эти данные запись CActiveRecord?
 

SiZE

Новичок
Template это отдельная таблица? (или всеже это тип модели <<interface>> а возможно даже свойство traits)
`template` - это отдельная таблица, сущность шаблон, а какой не важно. Это базовая таблица хранит PK для любого производного шаблона. У меня есть таблица `document`, которая хранит внешний ключ на таблицу `template`. Т.к. шаблонов можно описать неограниченное количество и таблиц для них, то не делать ведь в таблице `document` на каждый шаблон отдельное поле c FK. Простой пример:

Данные таблицы `template`:
id | user_id | title | type
1 | 12 | Мой шаблон договора | TemplateContract
2 | 12 | Мой шаблон акта | TemplateAct
3 | 12 | Мой шаблон счета | TemplateBill

Данные таблицы `document`:
id | user_id | type | template_id
1 | 12 | DocumentContractSale | 1
2 | 12 | DocumentContractWork | 1
3 | 12 | DocumentActFinish | 2

правильно ли я понимаю, ты хочешь, имея фабрику TemplateManager, создавать обьекты CActiveRecord?
Да. TemplateManager возвращает нужный производный класс, который собирает нужные мне данные. Ничего лучше не придумал. Может кто подскажет реализацию получше?

те. запись
PHP:
$model = TemplateManager::factory('Contract')->loadModel( $id );
должна вернуть строку с индификатором $id таблицы Contract, по типу CActiveRecord
при этом метод loadModel запрятан в специальном классе TemplateContractManager который (и только он) умеет заполнять ContractTemplate?
Метод loadModel должен быть у всех производных классов TemplateManager.

В yii такой вызов
PHP:
 Model::model()->findByPk( $id )
вернет объект модели заполненный данными (1 модель = 1 строка из таблицы), если они есть конечно.
 

WMix

герр M:)ller
Партнер клуба
класс Template имеет другую роль нежели TemplateContract TemplateAct или TemplateBill. (тут я путаюсь еще)

по минимуму так

PHP:
class TemplateContract extends CActiveRecord{
// кишки
}
* если только разговор идет о создании любой модели шаблон, пользователь, и другое то у нас тип уже имеется - CActiveRecord можно к нему привязаться (хотя ... )

TemplateManager играет сразу 3 роли, с одной стороны это абстрактная фабрика, с другой стороны конкретная а еще и базовая для фабрик типа TemplateContractManager
выделим интерфейс манагера
PHP:
interface TemplateManagerInterface{
  /**
   * @return CActiveRecord
   */
  public function loadModel( $id );
}
// отвязываемся от базового класса
class TemplateContractManager  implements TemplateManagerInterface{
    public function loadModel( $id ){
        return TemplateContract::model()->findByPk( $id );
    }
}
и опишем фабричный метод
PHP:
class TemplateManagerFactory {

  /**
   * @return TemplateManagerInterface
   */
  public static function factory( $template_class = '' /* заглушка для TemplateManager */){
    $class = 'Template'.$template_class.'Manager';
    // проверка на возможность
    if(class_exists( $class ) && is_subclass_of($class, 'TemplateManagerInterface' ){
      return new $class;
    }
    else throw ....
  }
}
// цепочка готова 
$model = TemplateManagerFactory::factory('Contract')->loadModel( $id );
дальше хотелось бы понять, нужно ли обьеденять TemplateContract, TemplateAct и TemplateBill в единый интерфейс (что общего у них?), а также нужно ли отделять Template
или как это все будет собираться (нужен ли общий метод для сборки)
 
  • Like
Реакции: SiZE
Сверху