私は最近、パフォーマンスと抽象化の問題のために Doctrine をやめて、データベース主導のロジックに移行しようとしています。私は主にPostgreSQLを使用しています。
教義
Doctrine で気に入った点の 1 つは継承で、これを Web アプリの複数のロールに使用しました。基本テーブル/クラス Person があり、すべてのロール (管理者、開発者、ユーザーなど) がこのクラスを拡張します。すべてのユーザーが 1 つのベース テーブルを共有するため、一意のログイン/識別子 (私の場合は電子メール) を保持するのに役立ちます。しかし、ドクトリンから人の情報を取得すると、そのすべてのプロパティを備えた最終的なクラスが得られました。例えば:
$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 つのクエリ
ベース テーブル people からロールを見つけて、ロールのテーブルから直接選択するのは非常に簡単です。
-- 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 つのクエリ
単一のクエリでユーザーに関する情報を取得できれば便利です。関数のドキュメントを深く掘り下げて何かを掘り下げましたが、最後まで到達できませんでした。リターンクエリを使用するのが良いと思いましたが、私の問題は、機能する結果のタイプを指定する必要があることですが、ユーザーの役割 (列とタイプの数が異なるテーブル) によって変わる可能性があります。
これは結果の 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を使用する必要がありますが、その方法では何も学びません. どんな助けでも大歓迎です。