DBIx-Class - join нескольких таблиц

| Комментариев: 6 | Нет трекбэков

 

 

notes.jpgХозяйке на заметку:

Таблица содержащая данные типа справочник (список позиций товара к примеру),

имеет связь с таблицей заказов. Связана по полю артикул. Артикул при этом не является primary-key для таблицы заказов, но является ключевым в таблице товаров.

Для выборки данных по JOIN из этих таблиц, делаем так (пример):

В описании таблицы Товаров:

__PACKAGE__->set_primary_key('артикул');
__PACKAGE__->belongs_to('имя_связи_произвольное',  'Класс::Имя_таблицы_заказов','артикул (поле из таблицы заказов)');

В описании таблицы заказов:

__PACKAGE__->set_primary_key('какой-то первичный ключ');
__PACKAGE__->has_many('имя_связи2', 'Класс::Имя_таблицы_товаров', { "foreign.артикул" => "self.артикул" });

В последнем анонимном хэше прописано что связь производится не по primary key (без указания слов foreign и self - связывать будет пытаться по primary).

Теперь работаем с запросами так:

 $rs = $db->{connect}->resultset('Таблица_заказов')->search(
        { 'me.любое_поле' => 'критерий_для_поиска' },
        {
          join      => ['имя_связи2'],
          '+select' => ['имя_связи2.поле_из_таблицы_товаров'],
          '+as'    => ['алиас_для_выборки'],        }
        );

# извлекаем данные

 while ( my $r = $rs->next ) {
   item  => $r->get_column('алиас_для_выборки');# выборка из связанной таблицы
   notes => $r->поле_из_таблицы_заказов;# выборка из родительской таблицы
   });
    }

Нет трекбэков

URL для трекбэков: http://perlmonks.org.ru/cgi-bin/MT/engine/mt-tb.cgi/40

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

Еще советик :) Что бы не мучаться и руками не писать все эти классы, можно сделать следующее.
Для начала во всех таблицах правильно создавать foreign keys и использовать DBIx::Class::Schema::Loader, в результате получится замечательно сгенерированные классы. Для примера выше таблица продукции:

CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

А в таблице заказов можно было указать внешний ключ:

CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL,
`amount` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_orders__product_id` (`product_id`),
CONSTRAINT `fk_orders__product_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8

после чего использовать автоматизацию:

#!/usr/bin/perl -w

package OrdersDB;

use strict;
use warnings;
use base qw/DBIx::Class::Schema::Loader/;

__PACKAGE__->loader_options(
use_namespaces => 0
);

1;

package main;

use DBIx::Class::Schema::Loader;

OrdersDB->dump_to_dir('./');
OrdersDB->connection("dbi:mysql:database=test;hostname=localhost", $USER, $PASS);

И в результате после выполнения получим два премилых класса (сгенерированные коменты вырезаны):

package OrdersDB::Product;

use strict;
use warnings;
use base 'DBIx::Class::Core';

__PACKAGE__->table("products");
__PACKAGE__->add_columns(
"id",
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
"name",
{ data_type => "varchar", is_nullable => 0, size => 255 },
);
__PACKAGE__->set_primary_key("id");

__PACKAGE__->has_many(
"orders",
"OrdersDB::Order",
{ "foreign.product_id" => "self.id" },
{ cascade_copy => 0, cascade_delete => 0 },
);
1;

И второй:

package OrdersDB::Order;

use strict;
use warnings;
use base 'DBIx::Class::Core';

__PACKAGE__->table("orders");
__PACKAGE__->add_columns(
"id",
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
"product_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
"amount",
{ data_type => "integer", is_nullable => 0 },
);
__PACKAGE__->set_primary_key("id");

__PACKAGE__->belongs_to(
"product",
"OrdersDB::Product",
{ id => "product_id" },
{ on_delete => "CASCADE", on_update => "CASCADE" },
);
1;

NB. Хочу заметить, что Loader хорошо распознаёт уникальность полей для связывания и правильно использует might_have/has_many/belongs_to.

После чего можно точно так же:

use OrdersDB;
# ...
my @orders = $sch->resultset('Order')->search({
'amount' => { '>=', 2 }
}, {
prefetch => 'products'
});

foreach my $order (@orders)
{
printf "OrderID: %d, ProductName: %s, Amount: %d\n",
$order->id,
$order->product->name,
$order->amount;
}

Мало того, prefetch не обязателен. Даже при указывании join DBIx::Class позволяет использовать подчиненные таблицы по имени связи без использования get_columns. Например, можно спокойно использовать вложенные join-ы.

my @orders = $sch->resultset('Orders')->search({
'product_description.name' => { 'like', '%bana%' },
'owner.location' => { 'in', \@locaion_ids },
'product.some_field' => { '>=', 1 }
},
{
join => {
'products' => {
'product_descriptions' => 'owner'
}
}
};
#...
$order->product->description->owner->name;
$order->product->description->long_desc;
$order->product->code;
$order->amount;

Prefetch в этом плане поможет избежать лишних запросов при доступе к подчиненным таблицам. А параметры '+select' и '+as' используются чаще всего для полей-функций, например:

'+select' => [ { count => 'amount', -as => 'amount_of_products' } ],
'+as' => [qw/prod_amount/],

Вот здесь очень хорошо про это всё написано:
http://cpan.uwinnipeg.ca/htdocs/DBIx-Class/Cookbook.html

Спасибо за развернутый комментарий.

Но при работе с Loader'ом у меня возникла проблема, точнее он не работал у меня для MSSQL2005 :(... ПОэтому пришлось писать все ручками, благо проект не большой. Для большого - либо мучать дальше лоадер, либо дублировать в mysql базу и с нее считывать лоадером...

Loader хорош, но иногда стоит понимать что к чему в том что он генерит.

Дык Loader генерит всё, как в документации по DBIx::Class. Главное правильно создать базу, указать ключи, и следовать лингвистическим правилам наименований, описанных в документации к Loader-у, тогда проблем не будет. В новой версии они таки ввели namespace-ы, очень удобно при работе с несколькими базами. А так же поправили работу с ключами и наименованиями связей.

2Monks:
С MS SQL не работал, но судя по гуглу изменения поступают достаточно часто, возможно уже поддерживается. Стоит посмотреть вот сюда:
http://kobesearch.cpan.org/htdocs/DBIx-Class-Schema-Loader/Changes.html
Возможно нужные возможности уже сделаны/пофикшены.

А если проблемы с Loader все еще не решены - докладывайте на #dbix-class@irc.perl.org. Трудно чинить когда все вроде бы работает :)

Хм. Надо попробовать обновиться.
Судя по:
Fix mssql common tests failures with MSSQL 2005 (skip test of datatypes found only on MSSQL 2008)
возможно новая версия будет работать.

Комментировать

Категории

Страницы


 


 

Page copy protected against web site content infringement by Copyscape

Об этой записи

Сообщение опубликовано 12.11.2010 11:23. Автор — Monks.

Предыдущая запись — Why Perl?

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