Kohana3.0 ORM. Связь «один-к-одному»

kohana Не так давно я писал пространную шпаргалку по работе с ORM. В той шпаргалке я решил пропустить связь «один-к-одному», видимо считая, что она никогда в жизни никому не понадобится, а если понадобится — то там делать не фиг, всё и так само собой сделается. Как это часто бывает — я ошибся.

Дело в том, что две связанные таблицы с такой связью логически можно просто объединить в одну таблицу. А раз есть такое мощное средство как ORM, в котором есть обработка такой связи, я уже размечтался было работать с такой связью как с одной таблицей. Но нет. Дудки. Всё вручную.

Итак, детали в студию.

Пример БД
===========================

one_to_one

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

В нашем примере это таблица пользователей и таблице информации о пользователях. В нашем случае объединение таблицы в одну было бы логично, но главная таблица используется модулем Auth, поэтому её лучше не трогать (хотя наверное лучше тронуть её..).

Описание связи
===========================
Модель создаётся довольно просто

 array(
                   'model' => 'userinfo',
                   'foreign_key' => 'user_id'
                   )
            );
} // End User Model

Также обязательно нужно создать модель связанной таблицы.

 array(
                   'model' => 'user',
                   'foreign_key' => 'user_id'
            )
            );
} // End User Model

Обратите внимание, что связь объявлена как $_belongs_to. Кстати, если не собираетесь из подчинённой таблицы лазить в главную можно эту связь тут не описывать. Достаточно пустой модели.

Чтение свойств
===========================
Чтение свойств делается средствами ORM просто замечательно.

$a=$user->userinfo->info1;

Пожалуй чтение — это единственное, что в нашем случае делается замечательно средствами ORM. Дальше всё сложнее.

Удаление записи
===========================
Удаление собственно записи из таблицы users само по себе не влечёт удаления записи из таблицы userinfos. Если конечно у вас не настроено удаление средствами СУБД по внешнему ключу.

В противном случае нужно удалять обе записи вручную. Иначе останется мусор.

$user ->userinfo->delete();
$user ->delete();

Редактирование записи
===========================
Сохранять нужно тоже обе записи отдельно.

$user=ORM::Factory('user',$id);

$user->username = $username;

$user->userinfo->info1 = $a;
$user->userinfo->info2 = $b;
$user->userinfo->info3 = $c;

$user->save();
$user->userinfo->save();

Создание записи
===========================
И создавать нужно отдельно

$user=ORM::Factory('user');
$user->username = $username;
$user->save();

$userinfo=ORM::Factory('userinfo');
$userinfo->user_id = $user->id; 
// После сохранения $user значение $user->id определено
$userinfo->info1 = $a;
$userinfo->info2 = $b;
$userinfo->info3 = $c;
$userinfo->save();

Вместо резюме
=========================
Вот такие пироги с котятами. Существенное усложнение кода, ухудшение его читаемости ..

А что делать — любая связь в БД требует определённых телодвижений. И порой задумаешься: может лучше избавиться от связи и свести всё в одну таблицу?

Kohana3.0 ORM. Связь «один-к-одному»: 7 комментариев

  1. Иван

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

    А хотя логически эти таблицы связаны и могут быть объединены в одну, их разделяют для оптимизации работы. «Старшая» таблица по сути неизменная, и используется только для чтения (username и прочее редко меняется). А все, что может регулярно изменяться (всякого рода счетчики входов и т.д.) выносится в отдельную таблицу, чтобы во время обновления главная не дергалась.

    Так что не все так страшно.

  2. altesack Автор записи

    @ Иван:
    Пришёл к выводу, что в моём случае разделение таблиц users-userinfo неоправдано.

    Тем более, что главная таблица users как раз регулярно изменяется. Поля last_login и logins.

  3. Иван

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

  4. Big_Shark

    Так должно работать
    $user=ORM::Factory(‘user’);
    $user->username = $username;
    $user->save();

    $userinfo=ORM::Factory(‘userinfo’);
    $userinfo->user = $user;
    $userinfo->info1 = $a;
    $userinfo->info2 = $b;
    $userinfo->info3 = $c;
    $userinfo->save();

  5. bvn

    Ряд замечаний, если позволите.
    1. По-моему в модели Model_User связь должна описываться так:
    protected $_has_one = array(
    ‘userinfo’ => array(
    ‘model’ => ‘userinfo’,
    ‘foreign_key’ => ‘id’
    )
    );
    поскольку в таблице users поле id является внешним ключом.

    2. С чтением свойств тоже не все так гладко, как хотелось бы, перед тем как их читать, их нужно загрузить:
    $user->userinfo->find();

    3. Методы save() и delete() можно перекрыть в модели Model_User так, чтобы не надо было делать 2 отдельных вызова:
    public function save()
    {
    $this->userinfo->save();
    }

    public funcion delete()
    {
    $this->userinfo->delete();
    }
    Как-то так, может не учел еще каких-то ньюансов, сам только начинаю с этим всем разбираться.

  6. bvn

    Ага… Затупил… :)
    Вовсе не собирался умничать, просто сам так делал и мне казалось, что я прав :)
    1 пункт — не верно, у вас верно.
    2 пункт — если верно сделан 1 пункт, то работает без предварительной загрузки…
    Ну может быть хоть по 3-ему пункту мое замечание было полезным :)

  7. bvn

    Черт подери… Я запутался, но похоже что в 1 пункте, я таки прав. Если там указать не id, а user_id, то не получится взаимно запрашивать свойства у связанной модели.

    То есть $user->userinfo будет работать, а $userinfo->user нет. А если уж то связь один-к-одному, то по-идее должно туда-сюда работать, нет?

Комментарии запрещены.