Шаблоны Symfony и Doctrine - установление отношений один ко многим - PullRequest
1 голос
/ 08 декабря 2010

В моей схеме БД у меня есть несколько таблиц, которые будут связаны с родительской таблицей. «Гадкий» способ решить проблему отношений состоит в том, чтобы вручную включить зависимости в схему:

sfArea:
  columns:
    id: integer
    name: string

sfCity:
  columns:
    name: string        
    area_id: integer
  relations:
    Area:
      class: sfArea
      local: area_id
      foreignType: many
      foreignAlias: Cities

sfItem:
  columns:
    name: string
    area_id: integer
  relations:
    Area:
      class: sfArea
      local: area_id
      foreignType: many
      foreignAlias: Items

Однако каждый раз, когда я добавляю класс, который будет присоединен к области, мне нужно будет добавлять отношение и все строки, которые идут с ним (copy / paste => future hell). Здесь я решил использовать Doctrine_Template, который позволяет мне достичь того же:

sfArea:
  columns:
    id: integer
    name: string

sfCity:
  actAs:
    AreaRelated: { foreignAlias: Cities }
  columns:
    name: string        

sfItem:
  actAs:
    AreaRelated: { foreignAlias: Items }
  columns:
    name: string

И шаблон класса:

class AreaRelated extends Doctrine_Template
{
    protected $_options = array(
        'foreignAlias'  =>  ''
    );

    public function setTableDefinition()
    {
        $this->hasColumn('area_id', 'integer');
    }

    public function setUp()
    {
        $this->hasOne('sfArea as Area', array(
                'local' => 'area_id',
                'foreign' => 'id',
                'foreignType' => 'many',
                'foreignAlias' => $this->_options['foreignAlias']
            )
        );
    }
}

Таблицы сгенерированы правильно, и отношение работает в направлении $ sfCity-> Area. Однако отношения, которые должны быть установлены в классе sfArea, не создаются ($ sf_area-> Cities выдает ошибку "Неизвестное свойство записи / связанный компонент" Cities "в" sfArea "").

Как можно создать другое отношение? Я даже попробовал это (без успеха):

//...
public function setUp()
{
    $thisTable = $this->_table;
    $areaTable = Doctrine::getTable("smArea");

    $thisTable->hasOne('smArea as Area', array(
            'local' => 'area_id',
            'foreign' => 'id',
            'foreignType' => Doctrine_Relation::MANY
        )
    );        

    $areaTable->hasMany($thisTable->getOption('name') . ' as ' . $this->_options['foreignAlias'], array(
            'local' => 'id',
            'foreign' => 'area_id',
            'foreignType' => Doctrine_Relation::ONE
        )
    );             
}

Ответы [ 3 ]

0 голосов
/ 17 декабря 2010

До сих пор, для записи, я предпочитаю сделать это вручную в файле схемы. Поскольку схема редко меняется, как упомянул Натан, я думаю, что это лучший способ сделать это, если Поведение не может быть реализовано (что я буду продолжать пытаться сделать как побочный проект). Преимущество подхода «Поведение» состоит в том, что он может автоматически и последовательно генерировать отношения к определенному центральному объекту по всем объектам, которые зависят от этого центрального объекта.

Другим полезным примером того, где это может быть полезно, является, например, движок блогов на нескольких сайтах, где все сообщения, пользователи, категории, загрузки мультимедиа ... должны быть связаны с конкретным блогом.

sfArea:
  columns:
    id: integer
    name: string

sfCity:
  columns:
    name: string        
    area_id: integer
  relations:
    Area:
      class: sfArea
      local: area_id
      foreignType: many
      foreignAlias: Cities

sfItem:
  columns:
    name: string
    area_id: integer
  relations:
    Area:
      class: sfArea
      local: area_id
      foreignType: many
      foreignAlias: Items
0 голосов
/ 20 декабря 2010

К сожалению, я не думаю, что есть способ сделать это, по крайней мере, без некоторых дополнительных определений *. Один из способов сделать это, требующий минимальных дополнений, - перечислить отношения в Районе с помощью параметра:

Area:
  options:
    models_with_areas: [Cities, Items]

Затем приложите отношения в Area::setUp

public function setUp()
{
  parent::setUp();
  $models = $this->getTable()->getOption('models_with_areas');
  foreach($models as $model)
  {
    $this->hasMany($model, array(
         'local' => 'id',
         'foreign' => Doctrine_Inflector::tablize($model) . '_id'
    ));
  }
}

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

Вопреки некоторым другим ответам, я думаю, что это разумный подход и потенциально хорошая идея. Главный принцип философии Symfony / Doctrine - «Не повторяйся сам». Это решение придерживается этой идеи. Это также обеспечивает значительные преимущества, если существует логика, совместно используемая классами, которые относятся к AreaRelated.

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

0 голосов
/ 17 декабря 2010

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

Мой совет: продумайте все заново и повторно проанализируйте, действительно ли необходимо нанять наемную работу в базе данных или, возможно, выразмышляем над объектами при моделировании реляционной базы данных.

С информацией, которую вы дали в примере, City выглядит как «Категории», поэтому, если вы поместите их в таблицу ссылок, можно будет классифицировать область, минимизируя репликацию кода.,Категория может иметь другие отношения или просто быть большой таблицей с большим количеством полей.

Итак, вот тип модели, которую я предлагаю (propel):

Area:
  tableName: area_table
  description: Area
  columns:
    id:
    name:
    category_id:
      type: integer
      foreignClass: Category
      foreignReference: id
      required: true

Category
  tableName: categor_table
  description: An area specialization
  columns:
    id:
    model_class_name:
      type: varchar(255)
      description: the model Class that will represent this piece of information
    field1:
    field2:
    relation:1

Использование mode_class_name в качестве подсказкиЧтобы узнать, что делать с необработанными данными, вы можете сделать что-то вроде:

CategoryClass >>
 public function getObjectRepresentation()
 {
   return new $this->getModelClassName()($this);
 }
[...]

RealObjectRepresentation1 >>
 public function __construct(Category $category)
 {
   //Initialize proper object using category information
 }

Таким образом, вы переместили DatabaseHirearchy в PhpObjectHirearchy

Надеюсь, это поможет вам, если вам понадобится большедетали я был бы рад помочь!

...