Я недавно пытаюсь отбросить Doctrine из-за производительности и проблем с абстракцией и перейти к логике, управляемой базой данных. Я использую в основном PostgreSQL.
Учение
Одна вещь, которая мне понравилась в Doctrine - это наследование, которое я использовал для нескольких ролей в веб-приложении. Существует базовая таблица / класс Person, и каждая роль (например, администратор, разработчик, пользователь) расширяет этот класс. Все пользователи используют одну базовую таблицу, поэтому она помогает сохранить уникальный логин / идентификатор пользователя (в моем случае это электронная почта). Но получение информации о человеке из доктрины привело к финальному занятию со всеми его свойствами. Например:
$user = $em->getRepository('Entities\Person')->findOneBy(array('email' => 'john.doe@example.com'));
if ( $user instanceof Entities\Developer) {
...
}
Хорошая особенность, но при наличии множества ролей результирующий SQL-запрос был очень неэффективным: выбор из оставленного базового класса, объединяющего все роли, а затем по определенному классу дискриминатора построил конечный класс из базовой и финальной таблиц.
PostgreSQL
Я обнаружил, что в postgres реализовано наследование таблиц, и оно работает хорошо. Но я хотел бы смоделировать поведение Doctrine, получив роль от db (не зная его роли и, следовательно, финального стола).
Для лучших примеров мои таблицы выглядят так:
--
-- base people table
--
CREATE TABLE people
(
id serial NOT NULL,
first_name character varying(25) NOT NULL,
last_name character varying(25) NOT NULL,
email character varying(50) NOT NULL,
"password" character varying(150),
CONSTRAINT people_pkey PRIMARY KEY (id)
);
--
-- role developer (does not have any role specific info)
--
CREATE TABLE developer
(
-- Inherited from table people: id integer NOT NULL DEFAULT nextval('people_id_seq'::regclass),
-- Inherited from table people: first_name character varying(25) NOT NULL,
-- Inherited from table people: last_name character varying(25) NOT NULL,
-- Inherited from table people: email character varying(50) NOT NULL,
-- Inherited from table people: "password" character varying(150),
CONSTRAINT developer_pkey PRIMARY KEY (id)
)
INHERITS (people);
--
-- role user
--
CREATE TABLE installer
(
-- Inherited from table people: id integer NOT NULL DEFAULT nextval('people_id_seq'::regclass),
-- Inherited from table people: first_name character varying(25) NOT NULL,
-- Inherited from table people: last_name character varying(25) NOT NULL,
-- Inherited from table people: email character varying(50) NOT NULL,
client character varying(50),
-- Inherited from table people: "password" character varying(150),
CONSTRAINT installer_pkey PRIMARY KEY (id)
)
INHERITS (people);
Решение 1 -> 2 запроса
Довольно просто выяснить роль людей из базовой таблицы, а затем выбрать непосредственно из таблицы роли:
-- returns name of table (and role) 'developer'
SELECT pg.relname
FROM people p, pg_class pg
WHERE pg.oid=p.tableoid and p.email = 'john.doe@example.com';
-- getting roles full info
SELECT *
FROM developer
WHERE email = 'kracmar@dannax.sk';
Это решение хорошо, но я искал более подходящее решение.
Решение 2 -> 1 запрос с использованием процедуры
Было бы неплохо получить информацию о пользователе всего за один запрос. Я углубился в документы функций и что-то выкопал, но не смог дойти до конца. Я подумал, что использовать запрос возврата было бы кстати, но моя проблема в том, что мне нужно указать тип результата для функции, но он может меняться в зависимости от роли пользователя (разные таблицы с количеством столбцов и типов).
Это один из результатов, функция возвращает запись, но это не запрос, один столбец со всеми полями в нем, разделенными запятой.
CREATE OR REPLACE FUNCTION get_person_by_email(person_email VARCHAR)
RETURNS record
LANGUAGE plpgsql
STABLE STRICT AS
$BODY$
DECLARE
role varchar;
result record;
BEGIN
SELECT pg.relname
INTO role
FROM people p,
pg_class pg
WHERE pg.oid=p.tableoid
AND p.email = person_email;
IF NOT FOUND THEN
RAISE exception 'Person with email % does not exists.', person_email;
END IF;
CASE
WHEN role = 'developer' THEN
SELECT *
INTO result
FROM developer
WHERE email = person_email;
WHEN ROLE = 'installer' THEN
SELECT *
INTO result
FROM installer
WHERE email = person_email;
END CASE;
RETURN result;
END;
$BODY$;
Выбор из этой функции невозможен, так как отсутствует определение столбцов. Может быть, я усложняю вещи и должен использовать решение 1, но таким образом я ничему не научусь. Любая помощь будет оценена.