Не так давно я писал пространную шпаргалку по работе с ORM. В той шпаргалке я решил пропустить связь “один-к-одному”, видимо считая, что она никогда в жизни никому не понадобится, а если понадобится – то там делать не фиг, всё и так само собой сделается. Как это часто бывает – я ошибся.
Дело в том, что две связанные таблицы с такой связью логически можно просто объединить в одну таблицу. А раз есть такое мощное средство как ORM, в котором есть обработка такой связи, я уже размечтался было работать с такой связью как с одной таблицей. Но нет. Дудки. Всё вручную.
Итак, детали в студию.
Пример БД
===========================

По организации базы данных связь “один-к-одному” реализуется так же как и в связи “один-ко-многим”. Точно также заводится внешний ключ, только договариваются на каждую запись в главной таблице заводить не более одной записи в подчинённой.
В нашем примере это таблица пользователей и таблице информации о пользователях. В нашем случае объединение таблицы в одну было бы логично, но главная таблица используется модулем Auth, поэтому её лучше не трогать (хотя наверное лучше тронуть её..).
Описание связи
===========================
Модель создаётся довольно просто
1 2 3 4 5 6 7 8 9 10 | <?php defined('SYSPATH') or die('No direct access allowed.'); class Model_User extends Model_Auth_User { protected $_has_one = array( 'userinfo' => array( 'model' => 'userinfo', 'foreign_key' => 'user_id' ) ); } // End User Model |
Также обязательно нужно создать модель связанной таблицы.
1 2 3 4 5 6 7 8 9 10 | <?php defined('SYSPATH') or die('No direct access allowed.'); class Model_Userinfo extends Orm { protected $_belongs_to = array( 'user' => array( 'model' => 'user', 'foreign_key' => 'user_id' ) ); } // End User Model |
Обратите внимание, что связь объявлена как $_belongs_to. Кстати, если не собираетесь из подчинённой таблицы лазить в главную можно эту связь тут не описывать. Достаточно пустой модели.
Чтение свойств
===========================
Чтение свойств делается средствами ORM просто замечательно.
1 | $a=$user->userinfo->info1; |
Пожалуй чтение – это единственное, что в нашем случае делается замечательно средствами ORM. Дальше всё сложнее.
Удаление записи
===========================
Удаление собственно записи из таблицы users само по себе не влечёт удаления записи из таблицы userinfos. Если конечно у вас не настроено удаление средствами СУБД по внешнему ключу.
В противном случае нужно удалять обе записи вручную. Иначе останется мусор.
1 2 | $user ->userinfo->delete(); $user ->delete(); |
Редактирование записи
===========================
Сохранять нужно тоже обе записи отдельно.
1 2 3 4 5 6 7 8 9 10 | $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(); |
Создание записи
===========================
И создавать нужно отдельно
1 2 3 4 5 6 7 8 9 10 11 | $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(); |
Вместо резюме
=========================
Вот такие пироги с котятами. Существенное усложнение кода, ухудшение его читаемости ..
А что делать – любая связь в БД требует определённых телодвижений. И порой задумаешься: может лучше избавиться от связи и свести всё в одну таблицу?
Посты по теме:
RSS-подписка
Не понял, в чем вообще проблема. Принципы те же, что и в случаях со множественными таблицами, поэтому и надо следить за каждой таблицей отдельно. Запись в roles_users тоже не удаляется автоматом, если убирается роль или пользователь.
А хотя логически эти таблицы связаны и могут быть объединены в одну, их разделяют для оптимизации работы. “Старшая” таблица по сути неизменная, и используется только для чтения (username и прочее редко меняется). А все, что может регулярно изменяться (всякого рода счетчики входов и т.д.) выносится в отдельную таблицу, чтобы во время обновления главная не дергалась.
Так что не все так страшно.
@ Иван:
Пришёл к выводу, что в моём случае разделение таблиц users-userinfo неоправдано.
Тем более, что главная таблица users как раз регулярно изменяется. Поля last_login и logins.
Ну уж эти-то поля точно лишние в основной таблице users. Это и есть те самые счетчики, которые и надо выносить отдельно, если говорить про оптимизацию.
Так должно работать
$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();
Ряд замечаний, если позволите.
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();
}
Как-то так, может не учел еще каких-то ньюансов, сам только начинаю с этим всем разбираться.
Ага… Затупил…

Вовсе не собирался умничать, просто сам так делал и мне казалось, что я прав
1 пункт – не верно, у вас верно.
2 пункт – если верно сделан 1 пункт, то работает без предварительной загрузки…
Ну может быть хоть по 3-ему пункту мое замечание было полезным
Черт подери… Я запутался, но похоже что в 1 пункте, я таки прав. Если там указать не id, а user_id, то не получится взаимно запрашивать свойства у связанной модели.
То есть $user->userinfo будет работать, а $userinfo->user нет. А если уж то связь один-к-одному, то по-идее должно туда-сюда работать, нет?