Pull to refresh

Comments 87

Иногда оно того не стоит
Кстати я эту коллекцию использую в паре с ActiveRecord отличнейшая штука получается.
Не поверите, это один в один то что я сделал.
Для общего развития посмотрите как работает ArrayObject, и почему я использовал именно его. Да и вообще на SPL посмотрите чтоб глупостей не говорить
С чего это вы взяли, что я не знаю, как работает ArrayObject? :) Я вам дал ссылку на готовое, давно существующее решение, которое, возможно, избавило бы вас от велосипедостроения.
1. Мой вариант проще
2. мой вариант быстрей (нативный класс будет явно быстрей чем набор интерфейсов)
3. мой класс не такой громоздкий и проще в поддержке
4. мой класс делает то же что и Ваш предложенный (в общих чертах)
Зачем мне менять мой «велосипед» на что-то более худшее?
Спасибо, поправил =) «предпросмотр» рулит
$stmt->fetch(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'ClassName'); 
Warning: PDOStatement::fetch() expects parameter 2 to be long, string given
Можно поподробней?
UFO landed and left these words here
Ладно, на самом деле не важно, Вы скажите это избавит от того что я описал:
1. Поля отдаются только через геттер
2. Конструктор вызывается только после отдачи всех полей
UFO landed and left these words here
Да, так уже лучше =) Благодарю. В свое время меня напрягало сильно что fetchObj заполняет класс до его создания.
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
> 2. Конструктор вызывается только после отдачи всех полей
Это и изменяется.

Вообще, Вызывать конструктор после установки значения свойств — это просто ппц, и любого нормального программиста это должно было натолкнуть на поиски как изменить данное поведение. Найти инфу про 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 создаётся при каждом обращении к одному индексу, у нас возвращаются ссылки на один и тот же объект
$users = Users::getAll();

if ($users[0] === $users[0]) {
  echo 'Trivial';
}
else {
  echo 'WTF?!'
}

Что напишет наша getAll, а что ваша? Какое поведение вы считаете логичным?
Бред какой-то написали.
Согласен =) протупил
Тогда уж так:

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! Да у него ведь есть волшебный метод fetchObj.
Да ведь не только у него!
Ничего что функции mysql_* будут убирать?
Ничего, потому что «объект произвольного класса можно выбрать через любое api работы с базами данных».
Что плохого что я хочу управлять тем как обьект получает данные?
Почему вы вдруг решили задать мне этот вопрос, никак не относящийся к нашему диалогу?
Смотрите, у вас в статье есть фраза:
> Первая мысль — PDO! Да у него ведь есть волшебный метод fetchObj.

Она не верна. Правильная фраза:
> Первая мысль — любой api доступа к данным! Да у них у всех есть волшебный метод fetchObj.

Все. При чем тут управление получением данных?
При том что PDO отдает данные одним способом — создавая свойства. Чтоб как-то контролировать эти анные без геттера и сеттера не обойтись. Меня именно это не устраивает.
Как интересный прием — да, имеет место, но по производительности все это печально выглядит. Все равно храниться будет ассоциативный массив, а конструкторы будут вызываться при обращении к свойствам, при чем, при повторном обращении — опять будут вызываться, а объекты будут дохнуть. Подумайте, что Вам важнее, просто заменить <?php echo $user['login']; ?> на <?php echo $user->login; ?> из-за нежной привязанности к ООП или в 2-3 выиграть в производительности кода? Попробуйте все приведенные в статье варианты прогнать 100000 раз и засеките microtime().

Гораздо лучше вариант с 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');
Разницы нет совершенно, разве что другой обьект работает с моим. Не Вы немного выше говорили что обьекты — ущерб производительности и даете мне обьект?
Я не даю объект, я даю статический метод, при этом инстанс объекта не создается, не происходит new. Статические класса позволяют удобно объединить несколько методов, за неимением неймспейсов в версиях PHP до 5.3
Я всетаки ошибся, не через любое, но через большинство популярных api можно. Нельзя, например в mssql.
Никогда не понимал зачем делать объект вместо массива, если отличаться будет только синтаксис:
$user->id вместо $user['id']

Ради эстетики?

Мне массивы нравятся, ассоциативные особенно. Использовать синтаксис $user['id'] удобнее, потому что я могу сделать $user[]. У объектов $user->$field_name выглядит странно, а использовать выражение вместо $field_name вообще нельзя.

Еще часто объекты используют для уникальности имен переменных, делая их свойствами объекта, прячут в класс. Особенно этим грешат всяческие фреймворки. Но на самом деле это задача namespace`ов.

По-моему, основное назначение и свойство объектов — инкапсуляция. (Да, наследование тоже важно и удобно, но не так как инкапсуляция. А полиморфизм в PHP я вообще не встречал :))
Если у вас в объекте нет методов для работы с его данными (простейшие геттеры, сеттеры не в счет), то вам нужен либо namaspace, либо ассоциативный массив.
> а использовать выражение вместо $field_name вообще нельзя.
Богат синтаксисом php:
for ($i =0; $i < 3; $i++) {
    echo $user->{'field_number'.$i}
}
М-да, век живи, век учись :)
Например для:
$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') — вот тут спорно, если полное имя нужно только в выводе, то да, наверное. Но если бизнес-логика на него завязана, то рендерить в доменной модели шаблоны как-то неправильно по-моему
Имею в виду, что load/save — методы загрузки и сохранения, и к предметной области не относятся, это лучше выделить в репозиторий, как Вы и сказали. Всякие методы типа restorePassword, signIn, checkPermission — это методы не объекта user, а системы безопасности, потом есть задачи шаблонизации и getFullName я отношу к ним. А вот если нужно сделать склонение фамилии, то это лучше реализовать в библиотеке, можно с статическом классе NameUtils. Что касается бизнес-логики, отвечающей за поведение именно этого объекта, это единственное, что в нем я оставляю. Например, методы buyProduct или applyToPosition.
Глянул в код, понял почему не так выглядит. У меня два метода — security::generatePasswordResetToken($user) и security::resetPassword($user, $token) :)

А getFullname я отношу всё же к вычисляемым свойствам, как, например, getTotal в объекте, представляющем строку заказа, а не к представлению. А вот какое представление есть у меня почти в каждом объекте модели предметной области, так это __toString()
Но не даром оно __*, это же по сути метод абстрактного класса (такого себе BusinessObject) и достаточно служебный.
Ну в основном, да, использую для прототипирования и отладки, в шаблонах echo $user; редко доживает до релиза и при первом замечании заказчика меняется не метод, а шаблон, иногда прямо в шаблоне, а если выражение сложное или встречается часто, то выносится в подшаблон.
Но вот если это действительно метод пользователя, то он должун быть у пользователя: $user->buy($product);
Только мне одному непонятно, зачем для языка, использующегося в основном для веб, нужны лишние слои абстракции? И лишние затраты памяти под это дело… Все хорошо в меру =).
Класс нативный, работать будет быстро.
Чтобы уменьшить затраты времени разработчика, не?
Не поверите, хороший разработчик стоит ~200$ в месяц а хороший сервак, которому плевать на любые мои абстракции ~200$ в месяц… Как думаете что более рационально, использовать удобный код с абстракциями или мега оптимизированный который неизвестно как просто будет поддердивать?
Вы хотели сказать — хороший разработчик — 2000..4000? Не индусов же советуете?
UFO landed and left these words here
Это лишь пример, никто не мешает отдавать ссылки, Вы еще скажите что я там не проверяю ключи и могут быть ошибки потому что выхожу за рамки массива =) Естественно это простейший вариант просто для демонстрации идеи.
Почему-то вспомнилась вторая стадия программизма:
«_Я разработчик. Да, именно так, я разработчик программного обеспечения, а не программист. Мое задание – разработать продукт, который будет стабильно работать и состоять из правильного кода. Код, который я пишу, должен работать сначала правильно, а потом уже быстро. Код должен быть структурированным и откомментированным – возможно его будут поддерживать другие разработчики. ООП – мой главный инструмент, я умею анализировать предметную область и выделять в ней иерархии классов. Все, что можно описать шаблонами или дженериками описывается шаблонами или дженериками. Я применяю декларативное прораммирование везде, где это возможно, ведь этот инструмент делает код максимально читабельным и понятным. Я использую кодогенерацию ведь это экономит мое время на написание кода. Я использую только новые технологии. Я провожу много времени в спорах с коллегами, какой паттерн или технология лучше подходят для решения поставленной задачи и правильно ли использовать здесь тот или иной подход..._»

Это описание. Данные анамнеза легко найдете сами habrahabr.ru/blogs/development/91665/
Учитывая что там всего три стадии, это выглядит больше чем комплимент =)
Вот и «больним» так кажется :) пока просветление не наступит
Вы ошибаетесь в градации и указав стадию =) В статье нет ни одного паттерна, в нем нет иерархий классов, нет «правильного кода». Я лишь показал на одно встроенное средство языка и указал одно из применений его, которое мне показалось довольно таки удобным на практике =)
Only those users with full accounts are able to leave comments. Log in, please.