Комментарии 87
Use ORM, Luke.
Иногда оно того не стоит
Кстати я эту коллекцию использую в паре с ActiveRecord отличнейшая штука получается.
github.com/limb-php-framework/limb/tree/master/active_record/src
для общего развития посмотрите, как тут коллекции реализованы.
для общего развития посмотрите, как тут коллекции реализованы.
Не поверите, это один в один то что я сделал.
Для общего развития посмотрите как работает ArrayObject, и почему я использовал именно его. Да и вообще на SPL посмотрите чтоб глупостей не говорить
Для общего развития посмотрите как работает ArrayObject, и почему я использовал именно его. Да и вообще на SPL посмотрите чтоб глупостей не говорить
С чего это вы взяли, что я не знаю, как работает ArrayObject? :) Я вам дал ссылку на готовое, давно существующее решение, которое, возможно, избавило бы вас от велосипедостроения.
<?php echo $user->id; ?>
в тегах бардак.
в тегах бардак.
парсер гад.
$stmt->fetch(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'ClassName');
Warning: PDOStatement::fetch() expects parameter 2 to be long, string given
Можно поподробней?
Можно поподробней?
Ладно, на самом деле не важно, Вы скажите это избавит от того что я описал:
1. Поля отдаются только через геттер
2. Конструктор вызывается только после отдачи всех полей
1. Поля отдаются только через геттер
2. Конструктор вызывается только после отдачи всех полей
> 2. Конструктор вызывается только после отдачи всех полей
Это и изменяется.
Вообще, Вызывать конструктор после установки значения свойств — это просто ппц, и любого нормального программиста это должно было натолкнуть на поиски как изменить данное поведение. Найти инфу про PDO::FETCH_PROPS_LATE — две минуты поисков по мануалу.
> 1. Поля отдаются только через геттер
Поля отдаются так, как хочется. Но ниже вы написали более подробно «Чтоб как-то контролировать эти анные без геттера и сеттера не обойтись» — вот на это можно подробнее и ответить. Классы, представляющие из себя строки в базе должны быть простыми максимально. Запихивать логику в конструктор таких классов — говнокод. Поэтому, да, нормальным будет создавать геттеры и сеттеры.
Это и изменяется.
Вообще, Вызывать конструктор после установки значения свойств — это просто ппц, и любого нормального программиста это должно было натолкнуть на поиски как изменить данное поведение. Найти инфу про PDO::FETCH_PROPS_LATE — две минуты поисков по мануалу.
> 1. Поля отдаются только через геттер
Поля отдаются так, как хочется. Но ниже вы написали более подробно «Чтоб как-то контролировать эти анные без геттера и сеттера не обойтись» — вот на это можно подробнее и ответить. Классы, представляющие из себя строки в базе должны быть простыми максимально. Запихивать логику в конструктор таких классов — говнокод. Поэтому, да, нормальным будет создавать геттеры и сеттеры.
Ну как вариант можно было всего лишь getAll() переписать так:
static public function getAll(){
$res = $this->db->fetchAll();
return array_map(function ($x){return (object)$x;},$res);
}
Тогда уж
return array_map(function ($x){return new User($x);},$res);
Я специально хочу избавиться от массива, у него, в отличии от обьтекта, нет свойств и методов.
тогда
return new ArrayObject(array_map(function ($x){return new User($x);},$res));
:)Внутри массив конечно мелькает, и даже два, но их обход реализован на C, а не на PHP
А я не то же самое по сути сделал?
Не совсем. У вас объект User создаётся при каждом обращении к одному индексу, у нас возвращаются ссылки на один и тот же объект
Что напишет наша getAll, а что ваша? Какое поведение вы считаете логичным?
$users = Users::getAll();
if ($users[0] === $users[0]) {
echo 'Trivial';
}
else {
echo 'WTF?!'
}
Что напишет наша getAll, а что ваша? Какое поведение вы считаете логичным?
Так лучше?
habrahabr.ru/blogs/php/125665/#comment_4140504
public function offsetGet( $index )
{
$this->$_cashe[$index] = new User( parent::offsetGet[$index] );
return &$this->$_cashe[$index];
}
habrahabr.ru/blogs/php/125665/#comment_4140504
Бред какой-то написали.
Тогда уж так:
public function offsetGet( $index )
{
if (!isset($this->$_cache[$index])) {
$this->$_cache[$index] = new User( parent::offsetGet[$index] );
}
return &$this->$_cache[$index];
}
Нет :) Результат тот же самый, только памяти больше жрёт.
Тогда уж так
Тогда уж так
public function offsetGet( $index )
{
if (!isset($this->_cache[$index]) {
$this->_cashe[$index] = new User( parent::offsetGet[$index] );
}
return $this->_cashe[$index];
}
Либо PDO::FETCH_CLASS, либо ORM типа Doctrine полностью решит ваши проблемы. И без всякого велосипедостроительства.
Как бы при чем тут PDO, если в php можно выбрать объект произвольного классам через любое api работы с базами данных.
www.php.net/manual/en/function.mysql-fetch-object.php
www.php.net/manual/en/function.mysql-fetch-object.php
Вы наверно совершенно не читали что я писал.
> Первая мысль — PDO! Да у него ведь есть волшебный метод fetchObj.
Да ведь не только у него!
Да ведь не только у него!
Ничего что функции mysql_* будут убирать?
Ничего, потому что «объект произвольного класса можно выбрать через любое api работы с базами данных».
Что плохого что я хочу управлять тем как обьект получает данные?
Почему вы вдруг решили задать мне этот вопрос, никак не относящийся к нашему диалогу?
Смотрите, у вас в статье есть фраза:
> Первая мысль — PDO! Да у него ведь есть волшебный метод fetchObj.
Она не верна. Правильная фраза:
> Первая мысль — любой api доступа к данным! Да у них у всех есть волшебный метод fetchObj.
Все. При чем тут управление получением данных?
> Первая мысль — PDO! Да у него ведь есть волшебный метод fetchObj.
Она не верна. Правильная фраза:
> Первая мысль — любой api доступа к данным! Да у них у всех есть волшебный метод fetchObj.
Все. При чем тут управление получением данных?
Как интересный прием — да, имеет место, но по производительности все это печально выглядит. Все равно храниться будет ассоциативный массив, а конструкторы будут вызываться при обращении к свойствам, при чем, при повторном обращении — опять будут вызываться, а объекты будут дохнуть. Подумайте, что Вам важнее, просто заменить <?php echo $user['login']; ?> на <?php echo $user->login; ?> из-за нежной привязанности к ООП или в 2-3 выиграть в производительности кода? Попробуйте все приведенные в статье варианты прогнать 100000 раз и засеките microtime().
Гораздо лучше вариант с PDO или mysql_fetch_object, как бы костылей не видно при этом, но поверьте, PHP не сделан от природы объектным, и хранение свойств объекта в памяти у него все одно сделано через ту же структуру, что и ассоциативных массивов. Тут лучше использовать тот вараинт, который работает быстрее и позволяет написать код короче.
P.S. Меня вот, например (после Delphi и C#) раздражала сперва эта стрелочка -> в PHP и коробило страшно от нее (дайте мою любимую точку «Object.Property»), но это все личные предпочтения, от них можно и нужно абстрагироваться.
Гораздо лучше вариант с PDO или mysql_fetch_object, как бы костылей не видно при этом, но поверьте, PHP не сделан от природы объектным, и хранение свойств объекта в памяти у него все одно сделано через ту же структуру, что и ассоциативных массивов. Тут лучше использовать тот вараинт, который работает быстрее и позволяет написать код короче.
P.S. Меня вот, например (после Delphi и C#) раздражала сперва эта стрелочка -> в PHP и коробило страшно от нее (дайте мою любимую точку «Object.Property»), но это все личные предпочтения, от них можно и нужно абстрагироваться.
Нечаянно комментарий не к статье привязал, сори. Это все Автору
Зачем дохнуть? Ссылки никто не отменял.
Оїект живет только одну итерацию форича. Разве этот массив вообще создается для одноразового использования? Если для многоразового, то лучше массив объектов, а если для одноразового, то лучше ассоциативный.
Сколько он живет — зависит от того где Вы его будете применять. Это лишь пример. Зачем вообще использовать фреймверки, ведь скрипт завершается при показе результата. Есть некие рамки удобства, и как бы не хочется писать на голом пхп все что можно сделать немного удобней, хоть и будет некий ущерб скорости. Это все не так сильно тормозит как кажется. Массив обьетктов от обьекта обтектов отличается на столько несущественно, так как основные рессурсы будут поедать именно содержимое, а не контейнер.
Если эстетическое удовольствие от вида -> вместо [] повышает у Вас производительность труда, то я согласен с таким решением )))
Вообще ArrayUtils::filter($MyArray), но в большинстве случаев это задача «where» в SQL-запросе, кроме тех случаев, когда чтение не из РСУБД.
И по каким полям Вы будете фильтровать? Там обьекты. Коллекция прекрасно знает что в ней находится и может просто орудовать данными внутри себя, это оболочка лишь для удобства. ArrayObject нативный класс, на скорость это сильно не повлияет
Коллекция ничего не знает о бизнес-логике, Вы все указываете:
$collection->filter( array( 'id' => '>5' ) );
и в чем разница:
ArrayUtils::filter($MyArray, array( 'id' => '>5' ));
или
ArrayUtils::filter($MyArray, 'id', '>5');
$collection->filter( array( 'id' => '>5' ) );
и в чем разница:
ArrayUtils::filter($MyArray, array( 'id' => '>5' ));
или
ArrayUtils::filter($MyArray, 'id', '>5');
Разницы нет совершенно, разве что другой обьект работает с моим. Не Вы немного выше говорили что обьекты — ущерб производительности и даете мне обьект?
Я всетаки ошибся, не через любое, но через большинство популярных api можно. Нельзя, например в mssql.
Никогда не понимал зачем делать объект вместо массива, если отличаться будет только синтаксис:
$user->id вместо $user['id']
Ради эстетики?
Мне массивы нравятся, ассоциативные особенно. Использовать синтаксис $user['id'] удобнее, потому что я могу сделать $user[]. У объектов $user->$field_name выглядит странно, а использовать выражение вместо $field_name вообще нельзя.
Еще часто объекты используют для уникальности имен переменных, делая их свойствами объекта, прячут в класс. Особенно этим грешат всяческие фреймворки. Но на самом деле это задача namespace`ов.
По-моему, основное назначение и свойство объектов — инкапсуляция. (Да, наследование тоже важно и удобно, но не так как инкапсуляция. А полиморфизм в PHP я вообще не встречал :))
Если у вас в объекте нет методов для работы с его данными (простейшие геттеры, сеттеры не в счет), то вам нужен либо namaspace, либо ассоциативный массив.
$user->id вместо $user['id']
Ради эстетики?
Мне массивы нравятся, ассоциативные особенно. Использовать синтаксис $user['id'] удобнее, потому что я могу сделать $user[]. У объектов $user->$field_name выглядит странно, а использовать выражение вместо $field_name вообще нельзя.
Еще часто объекты используют для уникальности имен переменных, делая их свойствами объекта, прячут в класс. Особенно этим грешат всяческие фреймворки. Но на самом деле это задача namespace`ов.
По-моему, основное назначение и свойство объектов — инкапсуляция. (Да, наследование тоже важно и удобно, но не так как инкапсуляция. А полиморфизм в PHP я вообще не встречал :))
Если у вас в объекте нет методов для работы с его данными (простейшие геттеры, сеттеры не в счет), то вам нужен либо namaspace, либо ассоциативный массив.
> а использовать выражение вместо $field_name вообще нельзя.
Богат синтаксисом php:
Богат синтаксисом php:
for ($i =0; $i < 3; $i++) {
echo $user->{'field_number'.$i}
}
Например для:
$collection->filter( array( 'id' => '>5' ) )
или
$collection->escape();
или
$collection->append()
итд, все заканчивается только Вашей фантазией. Так как это обьект, а не массив, то и добавить методов не составит труда.
Мне абсолютно не важен синтаксис, мне не нравятся массивы не потому что там скобочки и нравятся обьекты потому что стрелочки, а потому что работа с массивами сложней чем с обьектами. Нужно изменить работу обьекта? Проще простого, наследование + переопределение = новое действие.
полиморфизм Вы встречаете ежедневно при работе с PDO, с интерфейсами, с фабриками. На то он и полиморфизм чтоб его не замечать явно.
$collection->filter( array( 'id' => '>5' ) )
или
$collection->escape();
или
$collection->append()
итд, все заканчивается только Вашей фантазией. Так как это обьект, а не массив, то и добавить методов не составит труда.
Мне абсолютно не важен синтаксис, мне не нравятся массивы не потому что там скобочки и нравятся обьекты потому что стрелочки, а потому что работа с массивами сложней чем с обьектами. Нужно изменить работу обьекта? Проще простого, наследование + переопределение = новое действие.
полиморфизм Вы встречаете ежедневно при работе с PDO, с интерфейсами, с фабриками. На то он и полиморфизм чтоб его не замечать явно.
А полиморфизм в PHP я вообще не встречал :))
Пример полиморфизма в PHP
class A {
public function printSmth() {
echo 'foo';
}
}
class B extends A {
public function printSmth() {
echo 'baz';
}
}
Или вы перегрузку имели в виду?
Никогда не понимал зачем делать объект вместо массива, если отличаться будет только синтаксис:
$user->id вместо $user['id']
Например чтоб можно было вместо
$user['firstName'] . ' ' . $user['lastName']
написать $user->getFullName();
Если такое только в одном месте, то да, для эстетики, а если в двух, то уже DRY;А вообще вы конечно правы — если у объекта нет поведения, а только данные, то это какой-то неполноценный объект. Но, с другой стороны, если у нас 10 схожих по назначению сущностей, но у половины есть поведение, а у половины нет, то следуя принципу «не умножай сущности сверх необходимого» не следует вторую половину делать ассоциативным массивом, даже если полиморфизм не используется:
echo $man->name;
echo $cat->name;
по-моему поддерживаемее, чемecho $man->name;
echo $cat['name'];
Это дело вкуса, но я вот не люблю смешивать в объекте служебные функции, шаблоны вывода, данные, бизнес-логику. Например $user->load('username') или $user->restorePassword() или $user->getFullName я бы реализовывал примерно так: $user = db::load('user','username') и security::restorePassword($user) и template::($user, 'fullName'). Но это дело вкуса.
А чем бизнес-логика без данных будет оперировать? :)
Насчёт примеров:
— $user->load('username') я как-то даже не понял, что должна делать, то ли вернуть новый экземпляр, то ли свои свойства на новые поменять. User::load('username') ещё куда ни шло, но тоже не люблю с некоторых пор (хотя большой шаг вперёд по сравнению с mysql_fetch_array() ), предпочитаю что-то вроде $user = UserRepository->getOneByUsername('username').
— security::restorePassword($user) — тут согласен, наверное, как-то не так выглядит, не пойму
— template::($user, 'fullName') — вот тут спорно, если полное имя нужно только в выводе, то да, наверное. Но если бизнес-логика на него завязана, то рендерить в доменной модели шаблоны как-то неправильно по-моему
Насчёт примеров:
— $user->load('username') я как-то даже не понял, что должна делать, то ли вернуть новый экземпляр, то ли свои свойства на новые поменять. User::load('username') ещё куда ни шло, но тоже не люблю с некоторых пор (хотя большой шаг вперёд по сравнению с mysql_fetch_array() ), предпочитаю что-то вроде $user = UserRepository->getOneByUsername('username').
— security::restorePassword($user) — тут согласен, наверное, как-то не так выглядит, не пойму
— template::($user, 'fullName') — вот тут спорно, если полное имя нужно только в выводе, то да, наверное. Но если бизнес-логика на него завязана, то рендерить в доменной модели шаблоны как-то неправильно по-моему
Имею в виду, что load/save — методы загрузки и сохранения, и к предметной области не относятся, это лучше выделить в репозиторий, как Вы и сказали. Всякие методы типа restorePassword, signIn, checkPermission — это методы не объекта user, а системы безопасности, потом есть задачи шаблонизации и getFullName я отношу к ним. А вот если нужно сделать склонение фамилии, то это лучше реализовать в библиотеке, можно с статическом классе NameUtils. Что касается бизнес-логики, отвечающей за поведение именно этого объекта, это единственное, что в нем я оставляю. Например, методы buyProduct или applyToPosition.
Глянул в код, понял почему не так выглядит. У меня два метода — security::generatePasswordResetToken($user) и security::resetPassword($user, $token) :)
А getFullname я отношу всё же к вычисляемым свойствам, как, например, getTotal в объекте, представляющем строку заказа, а не к представлению. А вот какое представление есть у меня почти в каждом объекте модели предметной области, так это __toString()
А getFullname я отношу всё же к вычисляемым свойствам, как, например, getTotal в объекте, представляющем строку заказа, а не к представлению. А вот какое представление есть у меня почти в каждом объекте модели предметной области, так это __toString()
Но не даром оно __*, это же по сути метод абстрактного класса (такого себе BusinessObject) и достаточно служебный.
Но вот если это действительно метод пользователя, то он должун быть у пользователя: $user->buy($product);
Только мне одному непонятно, зачем для языка, использующегося в основном для веб, нужны лишние слои абстракции? И лишние затраты памяти под это дело… Все хорошо в меру =).
Класс нативный, работать будет быстро.
Чтобы уменьшить затраты времени разработчика, не?
Не поверите, хороший разработчик стоит ~200$ в месяц а хороший сервак, которому плевать на любые мои абстракции ~200$ в месяц… Как думаете что более рационально, использовать удобный код с абстракциями или мега оптимизированный который неизвестно как просто будет поддердивать?
Почему-то вспомнилась вторая стадия программизма:
«_Я разработчик. Да, именно так, я разработчик программного обеспечения, а не программист. Мое задание – разработать продукт, который будет стабильно работать и состоять из правильного кода. Код, который я пишу, должен работать сначала правильно, а потом уже быстро. Код должен быть структурированным и откомментированным – возможно его будут поддерживать другие разработчики. ООП – мой главный инструмент, я умею анализировать предметную область и выделять в ней иерархии классов. Все, что можно описать шаблонами или дженериками описывается шаблонами или дженериками. Я применяю декларативное прораммирование везде, где это возможно, ведь этот инструмент делает код максимально читабельным и понятным. Я использую кодогенерацию ведь это экономит мое время на написание кода. Я использую только новые технологии. Я провожу много времени в спорах с коллегами, какой паттерн или технология лучше подходят для решения поставленной задачи и правильно ли использовать здесь тот или иной подход..._»
Это описание. Данные анамнеза легко найдете сами habrahabr.ru/blogs/development/91665/
«_Я разработчик. Да, именно так, я разработчик программного обеспечения, а не программист. Мое задание – разработать продукт, который будет стабильно работать и состоять из правильного кода. Код, который я пишу, должен работать сначала правильно, а потом уже быстро. Код должен быть структурированным и откомментированным – возможно его будут поддерживать другие разработчики. ООП – мой главный инструмент, я умею анализировать предметную область и выделять в ней иерархии классов. Все, что можно описать шаблонами или дженериками описывается шаблонами или дженериками. Я применяю декларативное прораммирование везде, где это возможно, ведь этот инструмент делает код максимально читабельным и понятным. Я использую кодогенерацию ведь это экономит мое время на написание кода. Я использую только новые технологии. Я провожу много времени в спорах с коллегами, какой паттерн или технология лучше подходят для решения поставленной задачи и правильно ли использовать здесь тот или иной подход..._»
Это описание. Данные анамнеза легко найдете сами habrahabr.ru/blogs/development/91665/
Учитывая что там всего три стадии, это выглядит больше чем комплимент =)
Вот и «больним» так кажется :) пока просветление не наступит
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Массивы или Объекты? Хочу коллекции в пхп!