Создание вложенных отношений с ORM и минимизация запросов - PullRequest
3 голосов
/ 02 мая 2011

Редактировать 3

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

В Редактировать 2 Я думаю, что вложенные циклы - лучшее решение для выполнения

Total_queries = 1 + 2(Slides_in_project) + Shapes_in_project
                |                |                        \
  Query to get project           |                         \
                                 |                          \
        Query to get slides and double because of points     \
                                                              \
                                              Get all the shapes in the project

Мне бы хотелось получить лучший пример, потому что для наполнения простых проектов у меня, вероятно, будет 200-500 запросов.Это плохо.

Edit 2

Ну, я уже давно играю с этим, и у меня есть некоторые результаты, но я не думаю, что это "ПРАВИЛЬНЫЙ" способ, и это важно дляменя многоЧто я делаю, так это использую метод where_related, чтобы получить нужные объекты, но я думаю, что мой счетчик запросов все еще довольно высок, и я знаю, что ORM может работать лучше.Когда я использую where, чтобы создать правильную иерархию, я должен использовать вложенные циклы foreach, и мне это не нравится.Это означает бесполезные запросы.

Вот решение, которое я придумала

function get_project_points($link_id)
{
    echo "<pre>";
    foreach($this->where('link_id', $link_id)->get()->slide->where_related('project', 'id', $this)->get() as $slide){
        echo $slide->id."<br>";
        foreach($slide->shape->where_related('slide', 'id', $slide->id)->get() as $shape){
            echo "\t".$shape->id."<br>";
            foreach ($shape->point->where_related('shape', 'id', $shape->id)->get() as $point) {
                echo "\t\t".$point->id."<br>";
            }
        }
    }
}

Это приводит к хорошей многоуровневой структуре, и, как вы можете видеть, было бы легко заменить эхо на заполнение объекта / массива.

Я бы предпочел, однако, одну цепочечную команду, которая, если возможно, делала то же самое, так что определение области видимости тоже не проблема.

Некоторые цепочки, более похожие на

$this->where('link_id', $link_id)->get()
    ->slide->where_related('project', 'id', $this)->get()
    ->shape->where_related('slide', 'id', $slide->id)->get()
    ->point->where_related('shape', 'id', $shape->id)->get()

Это, конечно, не дает почти таких же результатов, как вложенные циклы foreach, но я хотел бы знать, можно ли связывать отношения и заполнять объекты без вложенного foreach

Так что я просто выполнил некоторое профилирование и вложилЦиклы foreach генерируют 63 запроса для небольшого проекта и занимают почти полсекунды для получения результатов.Это действительно слишком медленно.Должен быть лучший запрос.

__________ Редактировать 1

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

Я хотел бы просто повторить идентификаторы всех slides в project,Я дам список того, что я пробовал безрезультатно.Моя модель структуры такая же, как показано ниже. Я просто добавляю методы.

class Project extends DataMapper {
    var $has_many = array("slide");

    function get_project_slides($link_id)
    {
        $this->where('link_id', $link_id)
            ->where_related('slides', 'project_id' 
                $this->where('link_id', $link_id)->get()->id
            )
        ->get();
    }

}

И я попробовал то, что я считаю логичной противоположностью в методе слайдов.

Что я делаюнеправильно ... Как вы структурируете отношения ORM?


Оригинальный вопрос

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

Я использую DataMapper в качестве ORM с CodeIgniter .У меня установка работает нормально, и я прочитал все документы, я просто не могу понять, как получить информацию в контроллерах

+-----------+    +------------+    +---------+    +----------+
|  projects |    | slides     |    | shapes  |    |  points  |
+-----------+    +------------+    +---------+    +----------+
|    id     |    |  id        |    | id      |    | id       |
+-----------+    | project_id |    |slide_id |    | shape_id |
                 +------------+    +---------+    | x        |
                                                  | y        |
                                                  +----------+

Модели -

project.php

class Project extends DataMapper {
    var $has_many = array("slide");
}

//  End of project.php
//  Location: ./application/models/project.php 

slide.php

<?php

class Slide extends DataMapper {
    var $has_many = array("shape");
    var $has_one = array("project");
}

//  End of slide.php
//  Location: ./application/models/slide.php 

shape.php

<?php

class Shape extends DataMapper {
    var $has_many = array("point");
    var $has_one = array("slide");
}

//  End of shape.php
//  Location: ./application/models/shape.php 

точка.php

<?php

class Point extends DataMapper {
    var $has_one = array("shape");
}

//  End of point.php
//  Location: ./application/models/point.php 

Вышеуказанное должно создать нисходящую-> множество отношений между проектами-> слайды-> фигуры-> точки

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

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

Ответы [ 6 ]

0 голосов
/ 13 февраля 2013

Взгляните на Гранаду .

Из файла readme:

class User extends Model {
         public static $_table = 'user';

         public function post(){
               return $this->has_many('Post');
         }
         public function avatar(){
               return $this->has_one('Avatar');
         }
   }

Вы можете включить отношения в свои отношения!

$user_list = Model::factory('User')->with(array('post'=>array('with'=>'comments')),'avatar')->find_many();

Будет сделано 3 запроса:

SELECT * FROM user 
SELECT * FROM avatar WHERE user.id IN (......)
SELECT * FROM post WHERE user.id IN (.....)
0 голосов
/ 13 февраля 2013

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

Проверьте это sqlFiddle: http://sqlfiddle.com/#!2/9ed46/2

Схема ящика:

CREATE TABLE project
    (
     id int auto_increment primary key, 
     name varchar(20)
    );

CREATE TABLE slide
    (
     id int auto_increment primary key, 
     name varchar(20),
     project_id int
    );

CREATE TABLE shape
    (
     id int auto_increment primary key, 
     name varchar(20),
     slide_id int
    );


CREATE TABLE point
    (
     id int auto_increment primary key, 
     name varchar(20),
     shape_id int
    );

Запрос:

select project.id as project_id, project.name as project_name,
       slide.id as   slide_id,   slide.name as   slide_name,
       shape.id as   shape_id,   shape.name as   shape_name,
       point.id as   point_id,   point.name as   point_name

from project

left join slide on slide.project_id = project.id
left join shape on shape.slide_id   = slide.id
left join point on point.shape_id   = shape.id

where project.id = 1

Возвращает что-то вроде этого:

PROJECT_ID PROJECT_NAME SLIDE_ID SLIDE_NAME SHAPE_ID SHAPE_NAME POINT_ID POINT_NAME
1          fst_project  1       fst_slide   1        fst_shape  1        first_pt
1          fst_project  1       fst_slide   1        fst_shape  2        2nd_pt
...

Обрабатывая этот вывод, вы можете построить дерево объектов, которое вам нравится, все в одном запросе. Но эта пост-обработка может занять некоторое время. Вам придется проходить через каждую точку.

0 голосов
/ 02 мая 2011

Прежде всего: я ничего не знаю о собственной реализации ORM CI, но когда я вижу, что вы делаете, либо отсутствует какая-то функциональность, либо вы используете ее неправильно (из вашего редактирования № 2).

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

0 голосов
/ 02 мая 2011

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

class Projects {
    var slides = null;
    function getSlides() {
        if(slides == null) {
            slides = getSlidesForProject(this.id);
        }
        return slides;
    }
}

class Slides {
    var shapes = null;
    function getShapes() {
        if(shapes == null) {
            shapes = getShapesForSlide(this.id);
        }
        return slides;
    }
}

class Shapes {
    //... same as Projects.getSlides and Slides.getShapes
}

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

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

0 голосов
/ 02 мая 2011

Я не уверен, как Datamapper делает это, но у меня есть собственная модель GenericObject для Codeigniter, которая выполняет ORM следующим образом:

class Article extends GenericObject
{
    public $relationships = array ( "has_many" => array ("comments"), "has_one" => array ("users") );
}
class Comments extends GenericObject
{
    public $relationships = array ( "belongs_to" => array ("articles", "users"), "has_one" => array ("users") );
}
class Users extends GenericObject
{
    public $relationships = array ( "has_many" => array ("comments", "articles") );
}

Если я хочу получить все от пользователя, тогда я могу просто сделатьчто-то вроде:

$User = new User( $some_user_id );
$User->boot_relations("all");

foreach ($User->Comments as $Comment)
{
    echo $Comment->title."<br />";
    echo $Comment->body."<br />";
    echo "written by ".$User->username;
}

Так что это может быть довольно элегантно (или, по крайней мере, мне нравится так думать).

0 голосов
/ 02 мая 2011

Прежде всего, я извиняюсь за это, но DataMapper CodeIgniter фактически является вариантом шаблона ActiveRecord.

Если вам интересно, вы сравниваете реальный шаблон DataMapper со своим аналогом - ActiveRecord .Короче говоря, разница в том, что в шаблоне DM ваш Domain Object не знает тип (и даже существование) хранилища.Он используется таким образом, как $mapper->store( $user );.

"Сочетание композиции объекта с наследованием класса."© GoF

Тем не менее ..


Если я правильно прочитал примеры, то это должно работать так: (Я предполагаю, что отношения между«сущности» уже установлены)

class Project extends DataMapper
{
   // --- the rest of your stuff 

   public function get_all_points()
   {
      $points = array();

      $slides = $this->slide->get()->all;


      foreach ( $slides as $slide )
      {
         $shapes = $slide->shape->get()->all;

         foreach ( $shapes as $shape )
         {
            $points = array_merge( $point = $shape->point->get();
         }
      }

      return $points;

   }


}

Тогда вы можете использовать что-то вроде

$project = new Project;
$all_points = $project->where( 'id' , 1 )->get_all_points();

foreach ( $all_points as $point )
{
   $point->x = 0;
   $point->save();
}

Это должно собрать все th-й точки, связанные с проектом с ID1, и установите значение X в 0 и сохраните каждое в базе данных .. не то, чтобы любой здравомыслящий человек сделал бы это.



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

...