PHP ORM и оптимизированные запросы отношений

#php #zend-framework #orm #relationships

#php #zend-framework #orm #отношения

Вопрос:

Мне нужен PHP ORM для хорошей работы с отношениями. Пожалуйста, рассмотрите приведенный ниже код в Zend:

 $persons = new Persons();
$person = $persons->find(5)->current();
echo 'Name: '.$person->fullname;

$phones = $person->findDependentRowset('Phones');
foreach($phones as $phone)
    echo 'Phone: '.$phone->phonenumber; 
 

Или код ниже в xPDO:

 $person = $xpdo->getObject('Persons', 5);
echo 'Name: '.$person->get('fullname');

$phones = $person->getMany('Phones');
foreach($phones as $phone)
    echo 'Phone: '.$phone->get('phonenumber');
 

в обоих сценариях ORMs выполняет два запроса, как показано ниже:

 SELECT * FROM persons WHERE id=5;
SELECT * FROM phones WHERE person=5;
 

Это означает один запрос для основного объекта и один запрос для каждого отношения, но мне нужно использовать ОДИН запрос для основного объекта и его отношений!
xPDO может сделать это, как показано ниже:

 $person = $xpdo->getObjectGraph('Persons', '{"Phones":{}}', 5);
echo 'Name: '.$person->get('fullname');

foreach($person->Phones as $phone)
    echo 'Phone: '.$phone->get('phonenumber');
 

который выполняет этот запрос:

 SELECT * FROM persons
LEFT JOIN phones ON phones.person=persons.id
WHERE persons.id=5
 

Это здорово, но невозможно установить поля для получения из таблиц!
это означает, что в этом случае xPDO использует «SELECT * «, поэтому, если я получу объект и его 4 отношения, я получу все поля всех этих таблиц!

Итак, мне нужен ORM для выполнения запроса ниже, например, выше:

 SELECT persons.fullname , phones.phonenumber FROM persons
LEFT JOIN phones ON phones.person=persons.id
WHERE persons.id=5
 

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

Спасибо AHHP

Комментарии:

1. Что не так с Doctrine? Вы можете настроить свои объекты для выполнения отдельных запросов с объединениями, используя «нетерпеливую выборку». См . doctrine-project.org/docs/orm/2.0/en/reference /. … Кроме того, Zend_Db_Table это не ORM. В лучшем случае это реализация шаблона table-data-gateway

2. Спасибо, Фил. Doctorine тяжелый для небольших проектов! Не могли бы вы дать мне образец для моего примера? Как это сделать с (вне) DQL?

3. Я понятия не имею, что означает «тяжелый для небольших проектов». Это инструмент, вы его используете. Если вам нужен ORM в вашем проекте, Doctrine будет моим первым выбором. Также см. раздел Быстрая загрузка . Вы, по сути, добавляете fetch="EAGER" к своему сопоставлению ассоциаций. Кроме того, это Doctrine , а не Doctorine .

Ответ №1:

xPDO может это сделать. Вам просто нужно настроить свое мышление и свой запрос.

Помните, что ObjectGraph использует имя класса объекта, где как отношения в графе используются в графике и запросе.

  $criteria = $this->xpdo->newQuery('prefixClient');
            if (!empty($limit))
                $criteria->limit($limit);
            $criteria->where(array('Child.planid' => $this->getPrimaryKey(),
                                   'Child.active' => true,
                                   'GrandChild.someAttribute' => true,
                                   'GreatGrandChild.someOtherAttribute' => true,
                                   'suspended' => false
                             ));

            $out = $this->xpdo->getCollectionGraph('prefixClient', '{"Child":{"GrandChild":{"GreatGrandChild":{}}}}', $criteria);
 

Вы устанавливаете WHERE для любого аспекта отношения, включая текущий объект, как показано в строке suspended => false .

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

Ответ №2:

О боже,

Я ем этот корм для собак около 2 месяцев, и мне это нравится. RED BEAN.

http://www.redbeanphp.com/

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

http://www.redbeanphp.com/manual/nested_bean

Весь файл довольно маленький. Работает с SQL. Я использую его в масштабном проекте, и мне нравится, как быстро я могу все сделать.

Джон.

Комментарии:

1. RedBean полон потрясающего соуса.

Ответ №3:

Существует несколько способов, которыми Gacela может обрабатывать автоматическую выборку связанной информации:

1) Отношения наследования

В примере, подобном следующему:

 CREATE TABLE wizards (
    wizardId INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    fname VARCHAR(255) NOT NULL,
    lname VARCHAR(255) NOT NULL,
    ROLE ENUM('teacher', 'student') NULL,
    addressId INT UNSIGNED NULL,
    CONSTRAINT fk_address_wizard
    FOREIGN KEY (addressId)
    REFERENCES addresses(addressId)
    ON DELETE SET NULL
) ENGINE = Innodb;

CREATE TABLE students (
    wizardId INT UNSIGNED NOT NULL PRIMARY KEY,
    houseId INT UNSIGNED NOT NULL,
    isDAMembmer BOOL NOT NULL DEFAULT 0,
    CONSTRAINT fk_wizard_student
    FOREIGN KEY (wizardId)
    REFERENCES wizards(wizardId)
    ON DELETE CASCADE,
    CONSTRAINT fk_house_students
    FOREIGN KEY (houseId)
    REFERENCES houses(houseId)
    ON DELETE RESTRICT
) ENGINE = Innodb;
 

students Таблица имеет тот же первичный ключ, wizards что и таблица, и из-за определения отношения внешнего ключа Gacela обнаружит, что students наследует
все поля wizards .

2) Зависимые отношения

Вероятно, это ближе к тому, что вы ищете:

 CREATE TABLE addresses (
    addressId INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    locationName VARCHAR(255) NOT NULL
) ENGINE = Innodb;

CREATE TABLE wizards (
    wizardId INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    fname VARCHAR(255) NOT NULL,
    lname VARCHAR(255) NOT NULL,
    ROLE ENUM('teacher', 'student') NULL,
    addressId INT UNSIGNED NULL,
    CONSTRAINT fk_address_wizard
        FOREIGN KEY (addressId)
        REFERENCES addresses(addressId)
        ON DELETE SET NULL
) ENGINE = Innodb;
 

Однако этот пример все еще немного отличается от вашего примера, потому что AddressID находится в wizards таблице, что создает отношение belongsTo, а не hasMany,
что и отражает ваш пример.

В Gacela доступен третий вариант, однако я бы сначала посоветовал вам подумать о том, что, хотя часто предпочтительнее быстро извлекать данные, всегда быстрая выборка, а не отложенная загрузка данных, имеет реальные последствия для производительности. Именно по этой причине Gacela (и, в основном, любой другой Data Mapper или ORM) по умолчанию не извлекает данные из отношений hasMany. Тем не менее, вы могли бы написать что-то вроде следующего:

 class Mapper extends GacelaMapper 
{

    public function find($id)
    {
        $query = $this->_source()->query();

        $query->from('persons', array('fullName'))
            ->join('phones', 'phones.person=persons.id', array('phonenumber'), 'left')
            ->where('persons.id = :id', array(':id' => $id);

        $data = $this->_source->query($query);

        return $this->_load($data);
    }
}
 

Комментарии:

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