Comments 81
Жесткую привязку к 'id' надо заменить primary key
Еще вопрос будет ли оно корректно работать с relations child->parent и не лезть в базу за одним и тем же parent для 10 child?
В базу оно будет лезть точно так же как и раньше, преимущество приведенного метода только в том, что не будет путаницы с одинаковыми объектами разнесенными по всем скриптам. Т.е. оно будет лезть 10 раз, но объект parent будет создан только один.
Чтобы не лезло в базу нужно перехватывать момент «перед запросом», но без создания новой прослойки между моделью и CActiveRecord так, увы, сделать не получится.
Чтобы не лезло в базу нужно перехватывать момент «перед запросом», но без создания новой прослойки между моделью и CActiveRecord так, увы, сделать не получится.
Все уже украдено до вас github.com/phpnode/YiiBlocks/tree/master/identitymap и грамотней, раз тоже лезет в базу, то никаких преимуществ…
Метод по ссылке предполагает наследоваться от AActiveRecord, а это, согласитесь, как-то странновато…
Хотя это, пожалуй, недоработка фрэймворка, что нельзя по другому решить проблему.
Хотя это, пожалуй, недоработка фрэймворка, что нельзя по другому решить проблему.
Ну у меня модели итак наследуются от моей модели, что мешает мне мою модель унаследовать от AActiveRecord вместо СActiveRecord?
длинная цепочка наследования — это не есть хорошо.
Тесты и только тесты ;)
Опять же они и наследуются, чтоб не городить ваш код protected function instantiate в каждой модели
Опять же, в данном случае это недоработка фрэймворка.
И вот такое ConcreteModel < — AActiveRecord < — BActiveRecord < — CActiveRecord отнюдь не признак хорошей архитектуры =)
И вот такое ConcreteModel < — AActiveRecord < — BActiveRecord < — CActiveRecord отнюдь не признак хорошей архитектуры =)
А я думал это просто механизм ООП…
Как сделать лучше?
Что именно? Если Вы про такую структуру классов, то искать решения без наследования. С помощью тех же events. Но, как я писал ниже, нельзя перехватить событие перед отправкой запроса. Во время CActiveRecord::beforeFind() модель и все остальное пусто и нет возможности узнать по какому Id запросили модель(я по крайней мере не нашел).
getRecord принимает строку, а addRecord — объект. Послелний вариант, имхо, лучше.
Зачем нужно инстанцирование у ObjectWatcher, не лучше ли ограничиться статическими методами?
Зачем нужно инстанцирование у ObjectWatcher, не лучше ли ограничиться статическими методами?
на вид обычный Singleton
Странно то, что Active Record в Yii не включает в себя Identity Map.
Попробуйте им доказать, что оно им надо, очень удивитесь, мне хватило просьбы отключить double encode в htmlspecialchars…
Как раз одна из причин по которым все таки свой фреймворк бывает предпочтительнее в некоторых случаях. Благо есть время им заниматься.
Отключение double encode действительно штука очень сомнительная потому как поощряет делать лишний раз encode. Так можно и до отключения notice во view дойти.
С ним есть определённые проблемы. См., например, ниже про Rails.
1. Зачем два раза выбирать одну и ту же модель?
2. Какой смысл в indentity map, если всё-равно делается запрос к базе, даже если модель уже имеется.
2. Какой смысл в indentity map, если всё-равно делается запрос к базе, даже если модель уже имеется.
1. child->parent
2. какой смысл делать запрос в базу, если его результат в виде объекта уже есть?
2. какой смысл делать запрос в базу, если его результат в виде объекта уже есть?
1. М?
2. Например, для того, чтобы убедится, что результат более-менее актуален. Или для того, чтобы получить чистый объект, а не тот, с которым мы, ещё не сохранив, поработали.
2. Например, для того, чтобы убедится, что результат более-менее актуален. Или для того, чтобы получить чистый объект, а не тот, с которым мы, ещё не сохранив, поработали.
1. в одном месте вытянули модель через отношение, в другом месте обратно. Да разве не может быть случая, когда в рамках запроса требуется дважды работать с объектом в разных контекстах?
2. То что делается запрос — это недостаток, но его нельзя решить нормальным методом силами фреймворка, т.к. нет возможности перехватить момент «перед запросом».
2. То что делается запрос — это недостаток, но его нельзя решить нормальным методом силами фреймворка, т.к. нет возможности перехватить момент «перед запросом».
1. Может. Если это всё read, то проблемы нет. Если не только read, может стать сложно и плохо.
2. beforeFind выполняется перед запросом и в нём доступен критерий запроса.
2. beforeFind выполняется перед запросом и в нём доступен критерий запроса.
$this->getDbCriteria()? Если да, то в нем:
CDbCriteria#1
(
[select] => '*'
[distinct] => false
[condition] => ''
[params] => array()
[limit] => -1
[offset] => -1
[order] => ''
[group] => ''
[join] => ''
[having] => ''
[with] => null
[alias] => null
[together] => null
[index] => null
[scopes] => null
[CComponent:_e] => null
[CComponent:_m] => null
)
www.yiiframework.com/doc/api/1.1/CActiveRecord#beforeFind-detail там ниже написано под методом что вам нужно
А как обратиться к этому «скрытому» параметру? =)
Я думаю только подписавшись напрямую на событие onBeforeFind, затем уже его обрабатывать и брать из него criteria как $event->criteria; иначе вы просто перекрываете реализацию CActiveRecord beforFind();
А можно небольшой ошмёток кода для вопроизведения?
в модели
Вызываю через findByPk как указано в топике.
protected function beforeFind() {
CVarDumper::dump($this->getDbCriteria(),10,true);
}
Вызываю через findByPk как указано в топике.
SamDark, кстати, так надо или это баг, т.к. $criteria всегда будет null, т.к. ничего функции не передано, м? www.yiiframework.com/doc/api/1.1/CActiveRecord#beforeFind-detail Т.е. возможно в query нужно передавать в beforeFind() еще и $criteria? www.yiiframework.com/doc/api/1.1/CActiveRecord#query-detail
Да я все о том же, о наболевшем, что мои несчастные 10 child дергают из базы 10 однояйцевых parent, не более…
Умгу. Да, это нормальный, в общем, пример. Тут действительно пригодилось бы.
А разве кеширование не может решить ту проблему о которой пишет Dr_Death?
Ведь по большому счету эта проблема проектирования.
Ведь по большому счету эта проблема проектирования.
Может.
Т.е. даже Identity Map нету, потому что вдруг нужен актуальный объект и прочее, а мне предлагают объекты загнать наглухо в кэш. Помниться как то просил кэшить child->parent только на время выполнения скрипта, чтоб на одной странице много раз не вызывался и юзал результаты первого запроса, так и то столько про актуальность чего то там наговорили, а тут фигачить все в кэш и не жаловаться…
Тут смотря какая задача. При выборках обычно актуальность не так важна, как при изменении данных.
Я тут подумал, случай с parent-child тоже не сильно однозначный. Если нужно построить дерево, выбирать лучше всё и сразу. В остальных случаях, скорее всего, можно обойтись без identity map и дополнительной выборки, если поменять исходную точку запроса. Если более конкретный пример дадите, можно будет попробовать решить красиво без identity map.
Я тут подумал, случай с parent-child тоже не сильно однозначный. Если нужно построить дерево, выбирать лучше всё и сразу. В остальных случаях, скорее всего, можно обойтись без identity map и дополнительной выборки, если поменять исходную точку запроса. Если более конкретный пример дадите, можно будет попробовать решить красиво без identity map.
Т.е. 10 parent которые по сути один и тот же, но на самом деле 10 разных, можно изменять по разному в разных местах и при этом говорить о какой то актуальности измененных данных?
Мне пока lazy load нравиться больше, чем лазить каждый раз и дописывать join и прочее with вместо child->parent->parent
Мне пока lazy load нравиться больше, чем лазить каждый раз и дописывать join и прочее with вместо child->parent->parent
Зависит от конкретного примера. Может у вас вообще read-only дерево. Тогда можно что угодно делать и как угодно.
Lazy load так и так будет менее эффективен, чем eager. Даже с identity map. Это, конечно, если не кешировать.
Lazy load так и так будет менее эффективен, чем eager. Даже с identity map. Это, конечно, если не кешировать.
Как раз таки непонятно что надо делать с parent чтоб он был нужен в разных местах, еще и с разными данными внутри еще и не сохраненными, без абстрактных если бы да кабы.
lazy load удобней, посещаемость не 10 000 000 человек, чтоб начинать экономить
lazy load удобней, посещаемость не 10 000 000 человек, чтоб начинать экономить
Чистый объект можно получить через какой-нибудь getClean() от объекта, а обновить объект через, допустим, reload(). Вместо этого фреймворк выделяет память под два объекта, да еще и делает два запроса.
Еще было бы неплохо использовать UnitOfWork, но вряд ли это впишется в текущию концепую, да и в ActiveRecord в часности
Еще было бы неплохо использовать UnitOfWork, но вряд ли это впишется в текущию концепую, да и в ActiveRecord в часности
Смысл может быть в том что нужена именно актуальная версия объекта, а не закешированная при первом вызове, вообще тема топика как-то высосана из пальца и наталкивает на то что ТС еще не совсем понял Yii.
Dr_Death твой пример совсем сомнителен и глуп по большей части, никто из нормальных разработчиков такое не поддержит, у тебя просто «глубокая обида» на то что твой «важный» патч не приняли?
Dr_Death твой пример совсем сомнителен и глуп по большей части, никто из нормальных разработчиков такое не поддержит, у тебя просто «глубокая обида» на то что твой «важный» патч не приняли?
Ну наверно тогда проще дернуть один раз с параметром отключающем кэширование когда это действительно надо, чем постоянно дергать одно и тоже, когда это не надо. А дергать X раз parent для child->parent для вывода например ссылки на child, конечно не глупо. Обиды никакой, дописал руками и забыл про косяки, это проще и быстрей, чем слушать сказку про бычка.
ИМХО если Вам нужен IdentityMap, то у Вас проблемы в архитектуре приложения. Не понимаю зачем люди плодят название, если это по сути патерн Register…
Рекомендую почитать: «Патерны Проектирования» Гамма Эрик
Вы скрывает логику в защищенном методе, последующее поведение экземпляра класса не очевидно.
Рекомендую почитать: «Патерны Проектирования» Гамма Эрик
Вы скрывает логику в защищенном методе, последующее поведение экземпляра класса не очевидно.
Identity Map нужен, чтобы без проблем вызывать всякие $user->getArticles() несколько раз не кешируя в переменную, да и просто вызывать ->findByPk($item_id) зная, что не будет лишнего запроса. Почему-то JPA, Hibernate, Entity Framework, Rails Activerecord этого придерживаются, но Yii упирается рожками.
По крайней мере в Rails эта штука по-умолчанию выключена. В комментариях написано «не включать, убьёт». В документации приведены примеры удаления лишних записей при включении.
Угу, только там проблема с ассоциациями. Для единой модели все очень даже живуче. Плюс рельсы еще и сам запрос скеширует id=`id`
Как раз оттого, что всё очень нетривиально для связей, этого ещё нет в Yii.
С чтением тут всё ясно и хорошо. Проблемы могу всплыть при записи.
Почему бы явно не положить объект в реестр?
Зачем мешать логику реестра и ектив рекорд? Будут потом проблемы с тестированием;
А если хотите вызывать $user->getArticles() создайте свойство $_articles;
$record = $someResponsibleObject->getRegister()->get(10);
if (!$record) {
$record = $repository->findByPk(10);
$someResponsibleObject->getRegister()->add($record->getId(), $record);
}
Зачем мешать логику реестра и ектив рекорд? Будут потом проблемы с тестированием;
А если хотите вызывать $user->getArticles() создайте свойство $_articles;
public function getArticles() {
if (!$this->_articles) {
$this->_articles = parent::getArticles();
}
return $this->_articles;
}
Потому что хочется сохранить интерфейс нетронутым. Полиморфизм, знаете ли. Т.е.не клепать костыль при вызове, а изменить поведение, оставив интерфейс прежним.
>> Зачем мешать логику реестра и ектив рекорд?
Звучит как «зачем мешать mvc и?
>> Зачем мешать логику реестра и ектив рекорд?
Звучит как «зачем мешать mvc и?
> Не понимаю зачем люди плодят название
В той же книге Гаммы Эрика написано, что многие паттерны очень похожи, но преследуют разные цели и из-за этого имеют разные названия (можно вспомнить про bridge/adapter/proxy из паттернов проектирования).
В той же книге Гаммы Эрика написано, что многие паттерны очень похожи, но преследуют разные цели и из-за этого имеют разные названия (можно вспомнить про bridge/adapter/proxy из паттернов проектирования).
Эрих сотоварищи хотели как лучше, предположили, что будет рай, если дать каждому подходу по имени. Одного не учли эти парни с благими намерениями — есть в мире люди, тонко чувствующие всевозможные оттенки чего бы то ни было. А какой художник откажется дать имя, найденному им самим оттенку, ведь это шанс прикоснуться к великому!
Столкнулся с одной проблемой. Большой расход памяти. Любой выбранный объект остаётся в оперативной памяти, так как мы не знаем, нужен ли нам будет ещё объект или нет, мы его все равно храним… типа, а вдруг понадобится.
Угу, причем если вдруг понадобится, будь добр имей ссылку на этот объект, потому что выборка этих объектов повторно через ActiveRecord наплодит еще копий.
Как быть не знаешь? )
Я стараюсь сделать Service Layer, который как бы прослойка между контроллерами и моделями, и стараюсь не вызывать апи ActiveRecord кроме как из Service Layer, а из контроллера вызываю что-то типа $articleService->getLastArticles(5). Если память начнет быстро уходить или запросы станут тяжелыми, я всегда могу внутри метода сервиса переписать джоин через активрекорд на чистый sql или сделать кеширование в переменную.
Именно по причине тяги к энтерпрайз паттернам меня смущают виджеты, которые лезут в базу (помоему это противоречит даже MVC) и наличие знаний о базе и о маппинге в самой модели.
Именно по причине тяги к энтерпрайз паттернам меня смущают виджеты, которые лезут в базу (помоему это противоречит даже MVC) и наличие знаний о базе и о маппинге в самой модели.
А у вас Service Layer наружу отдает объекты СActiveRecord?
Мне тоже нравится идея промежуточного слоя, но вот модели с их богатым интерфейсом несколько портят картину. Разделить бы CActiveRecord на Domain Model (CModel) и Data Mapper (CActiveFinder + insert/update логика).
Мне тоже нравится идея промежуточного слоя, но вот модели с их богатым интерфейсом несколько портят картину. Разделить бы CActiveRecord на Domain Model (CModel) и Data Mapper (CActiveFinder + insert/update логика).
Стараюсь всякие файндеры и прочую «статику» держать в сервисе, а отдавать — объект CActiveRecord. В методы модели стараюсь выносить всякие простые вещи вроде $article->getTopComments().
Насчет Domain Model это конечно уже к симфони + doctrine. Правда вот во всем этом кроется то, что мне не нравится в стаке PHP — все решения, перетянутые с JPA или рельсы выглядят громоздко. Субъективно, конечно.
Насчет Domain Model это конечно уже к симфони + doctrine. Правда вот во всем этом кроется то, что мне не нравится в стаке PHP — все решения, перетянутые с JPA или рельсы выглядят громоздко. Субъективно, конечно.
В Yii2 finder отдельно, модель отдельно.
Это хорошо. А насколько модель отдельно? Кто сохраняет изменения в БД, модель или finder? Модель знает что-нибудь про БД?
Sign up to leave a comment.
Реализация шаблона Identity Map в Yii Framework