Как стать автором
Обновить

Комментарии 140

Видимо статья про traits? Я долго ломал голову над тем, что такое типажи, пока не сходил по ссылкам.
А еще я в теги добавил, на всякий случай. Но это не отменяет того факта что трейты это типажи. На всякий случай пометил в начале статьи.
Я б «типажи» на «трейты» все же заменил. Вы же composer композитором не называете
Ну тут можно поспорить. Общепризнанное названия такого рода структур все же типажи (покрайнемере так их называют в тех же C++, Java). А вот composer Это уже имя собственное. Я не филолог, просто о типажах я впервые услышал именно из C++ и именно в таком варианте названия.
Пусть это будет называться как угодно. В любом случае я подпишусь — да, это то, чего мне постоянно не хватало (множественное наследование). Да. наверняка сейчас есть сложности в поиске откуда ноги растут, но наверняка появятся новые методы мониторинга классов, и проблема исчезнет. Но то, что они это сделали уже сейчас — реально прорыв!
А можете накидать пару-тройку примеров, когда их Вам не хватало?
Основной момент, это именно множественная наследовательность. попробую объяснить на пальцах.
Вот у нас есть класс Животное (буду по русски писать, чтобы не переводить тем, кто не знает англ.)
Есть классы Домашнее_животное и Дикое_животное (производные от Животное).
Есть классы Кошка, Олень.

Сейчас приходится наследственность Собаки и Кошки выглядит так:
class Домашнее_животное extends Животное{}
class Кошка extends Домашнее_животное{}

Так же и Олень
class Дикое_животное extends Животное{}
class Олень extends Дикое_животное{}

И этих последовательных расширений может быть довольно много.
В предложенном же методе можно так писать

class Кошка{
use Животное, Домашнее_животное;
}

А если исходных классов штук 20? Можно довольно гибко всякие кастомные классы получать. Допустим
class Кошка{
use Животное, Домашнее_животное, Кошачьи;
}
Не самый удачный пример.

Во первых зачем use Животное и Домашнее_животное если Домашнее_животное уже Животное.
Просто я скорее всего двусмысленно выразился здесь:
Есть классы Домашнее_животное и Дикое_животное (производные от Животное).

На самом деле эти классы мы не делаем сразу extends. Их мы наследуем тогда, когда классическим образом идет разработка.
Сейчас приходится наследственность Собаки и Кошки выглядит так:
class Домашнее_животное extends Животное{}
class Кошка extends Домашнее_животное{}

В предлагаемом же варианте эти классы не расширяют сразу базовые классы. То есть весь код выглядит так:
trait Животное{}

trait Дикое_животное{}
trait Домашнее_животное{}
trait Кошачьи{}
trait Парнокопытные{}
и т.д.

class Кошка{
use Животное, Дикое_животное, Кошачьи;
}
Отличный метод отстрелить себе яй ногу.
И зачем

Есть классы Домашнее_животное и Дикое_животное (производные от Животное).


Не проще ли обойтись одним классом Животное с полем boolean дикое?
Аналогично, есть сомнения в необходимости остальных перечисленных классов.
Может тогда привести в пример несколько тысяч строк кода того же MODX-а, чтобы показать, зачем перешли от использования просто классов к абстрактным, и какую множественное наследование может добавить гибкость?
Это очень простой пример и понятно дело, что далеко не все идеально. Но его не надо буквально воспринимать, а надо просто идею видеть.
Пусть меня запинают минусами, но xPDO — одно из самых гениальных решений, с которыми мне приходилось работать. И я не просто любитель xPDO, а очень хорошо знающий его, потому знаю, о чем говорю.
Дело не в xPDO (Это же что-то типа библиотеки ORM для PHP?). А дело в архитектуре вашей CMS.

Почему так много классов наследуют объект работы с БД? Где инкапсуляция? Где абстракция от ORM слоя? Почему modEvent, modActiveUser, modScript и т.д. могут невозбранно лезть в БД? Чем это лучше олдового кондового пхп где можно было в любом месте делать mysql_query?

Если весь этот ужас приправить примесями, боюсь даже представить, насколько усугубится этот бардак.

Многие не понимают смысла xPDO как раз потому, что думают, что весь их смысл только во взаимодействии с DB. Но это не так. Все взаимодействие с БД выполняется на уровне пары классов, основной из которых xPDOQuery. Именно он выполняет взаимодействие с DB посредством PDO, расширением которого и является xPDO.

Все же производные от xPDOObject — это не просто объекты для взаимодействия с DB, а полноценные самостоятельные объекты, при чем отлично расширяемые. Но как это часто бывает в веб-проектох, эти сущности хранят свою информацию в базах данных.

К примеру возьмем класс modUser. Понятно дело, что все записи пользователей хранятся в DB. И допустим, мы хотим получить определенного пользователя. Мы выполняем $user = $modx->getObject('modUser', $id); Но в данном случае мы получим не просто данные пользователя, а именно объект пользователя. То есть xPDO получит данные пользователя, и вернет соответствующий объект с этими данными. Если бы нам нужны были только данные пользователя, мы могли бы просто выполнить запрос и получить данные, при этом возможно полностью без класс modUser.

Но работа с таким объектами реально удобна. Получил объект, выполнил какие-то действия с ним, изменил какие-либо его значения, и сохранил его $user->save(); При этом эти объекты могут быть наделены своими уникальными методами, и можно влиять и на все процессы, в том числе установки и чтения переменных, удаления, сохранения и т.п. Но довольно сильная сторона — возможность расширения базовых объектов так, чтобы использовать таблицу исходного объекта. Таким образом мы и функционал расширяем, и базу данных не раздуваем, и за айдишниками не следим. Довольно наглядно про это написано здесь.

Плюс к этому очень мощная штука — связи объектов. То есть я на уровне ключей объектов могу прописать связи, и прописать зависимости (к примеру, удалять зависимый объект или нет). И в дальнейшем, к примеру, вот так работать: $profile = $user->getOne('Profile');
При этом вообще не приходится следить за первичными и вторичными ключами, именами колонок и т.д.

В общем я много что могу рассказать про сильные стороны xPDO. И хочу отметить, что довольно богатый опыт у меня не только с MODX, потому я оцениваю именно в сравнении, а не просто так.

Ну и довольно большим аргумент будет вот этот топик с видюшкой: community.modx-cms.ru/blog/social-engine/9493.html
Аргумент: на все про все написано менее тысячи строк кода. Это при том, что там очень много всяких проверок, управление из админки и т.п.
НЛО прилетело и опубликовало эту надпись здесь
Я не ORM отстаиваю, а говорю о реальной сути xPDO и его плюсах.
Если хотите намекнуть на то, ORM — это не по религии, и что pure SQL — это все, и я должен только его проповедовать, то не стоит утруждаться. Я год весь биллинг сотовой компании поддерживал, и с Ораклом научился работать на год раньше, чем с мускулом познакомился, и на чистом SQL, и PL/SQL пишу запросы и т.п. очень хорошо, чтобы что-то с чем-то сравнивать.
НЛО прилетело и опубликовало эту надпись здесь
Ваше право толковать все так, как вам хочется. И честно — феолетово, что вы там думаете об ORM. Насмотрелись уже всяких противников ORM. Пишите на чем хотите.
Поддержу nateless — если отбросить детали типа имен классов, то вы говорите о преимуществах практически любой ORM. А вот чем xPDO лучше, например, моей любимой Doctrine или, скажем, Propel — ни слова не увидел.
Я не говорю, чем оно лучше. Я показываю его суть, плюс связку с MODX.
А вот чем xPDO лучше, например, моей любимой Doctrine или, скажем, Propel — ни слова не увидел.
Не работал ни с одной, ни с другой. Но посмотрел этот мануал: propelorm.org/documentation/04-relationships.html
В глаза брасается вот это:
$author = new Author();
$author->setFirstName("Leo");
$author->setLastName("Tolstoy");
// no need to save the author yet

$publisher = new Publisher();
$publisher->setName("Viking Press");
// no need to save the publisher yet

$book = new Book();
$book->setTitle("War & Peace");
$book->setIsbn("0140444173");
$book->setPublisher($publisher);
$book->setAuthor($author);
$book->save(); // saves all 3 objects!

А что, все работает только на каких-то уникальных прописанных методах типа ->setTitle() или setIsbn()?
Разве нельзя просто $book->set('title', $title); и $book->set('Isbn', $Isbn);?
Или так?
$book->fromArray(array(
    'title' => $title,
    'Isbn' => $Isbn,
));

То есть оперируя просто именами переменных, а не создавая уникальные методы?

И вот это: $book->setAuthor($author); (Добавляет объект)
Разве нельзя типа $book->addOne($author); и чтобы ORM-ка сама разобралась что это за объект и куда его сунуть?
Ручками эти методы писать не нужно — кодогенераторы по схеме БД или, емнип, генерация схемы по чему-то

Лично мне отдельные акцессоры для каждого поля больше нравятся — автодополнение в IDE, phpdoc, статический анализ и прочие плюшки «статической» типизации.

setAuthor предполагает что связь один к одному. addOne этого не предполагает. По имени функции я не могу преположить, что произойдт если я сделаю два вызова подряд если в схеме связь задана 1:1
Ручками эти методы писать не нужно — кодогенераторы по схеме БД или, емнип, генерация схемы по чему-то
Мне этого уже достаточно для того, чтобы сказать, что xPDO лучше представленных ORM-ок, так как нет универсальности, а код разрастается.

Лично мне отдельные акцессоры для каждого поля больше нравятся — автодополнение в IDE, phpdoc
В xPDO никто не ограничивает в этом. Своему объекту можно сколько угодно собственных методов прописать.
function setTitle($title){
    return $this->set('title', $title);
}

setAuthor предполагает что связь один к одному. addOne этого не предполагает.
Как раз очень даже предполагает. И это четко описывается в модели объекта. Если же для него связь прописана один-ко-многим, то используется метод ->addMany(), при чем в таком случае метод ->addOne() для него не выполнится.
Универсальные методы есть, но я их ни разу не использовал. Или обычные акцессоры или тупо публичное свойство без всякой магии.

Я должен ручками писать акцессоры для каждого из полусотни полей?

Не знаю как у вас предполагает. Для меня addOne предполагает добавить одну (из многих) запись за раз, а addMany — сразу много (aka пакетное добавление). Имхо, имя метода должно говорить основные концепции без анализа кода класса. setAuthor мне говорит, что автор может быть только один и этим методом он будет установлен (или перезаписан, если уже есть), а addAuthor (или authors->add) мне говорит, что авторов может быть много, а этим методом автор будет добавлен (неоднозначность возникает только если такой автор уже есть, но обычно хватает беглого взгляда на код, чтобы выяснить набор это или множество)

Я должен ручками писать акцессоры для каждого из полусотни полей?
В том-то и дело, что в xPDO этого вообще не требуется. Я всего-лишь показал, что при желании свои методы добавить всегда можно.

По поводу имен методов вообще не буду спорить.
Пускай не требуется, но я желаю — ручками должен писать или есть стандартная тулза, которая проанализирует метаданные и «напишет» мне все эти методы?
Какие именно «эти»? Хотите данные для колонки указать/изменить, выполнили $object->set($field, $value);
Есть массив данных и хотите все их «скормить» объекту? Пожалуйста. $object->fromArray($data);
Все данные объекта вы можете получить через $object->toArray() или конкретно $object->get($field).
При этом при сохранении он сохранить только те колонки, которые у него прописаны в мета.

Хотите объект добавить, тоже не вопрос. $object->addOne($anotherObject) или если связь один ко многим $object->addMany($objectsArray);

А свои методы пишутся только для уникального функционала. К примеру я хочу обрабатывать особым способом установку значения для какого-то поля. Тогда я в классе объекта пропишу.
function set($field, $value){
    switch($field){
         case 'author':
              $value = "Mr.(Ms.) {$value}";
              break;
    }
    return parent::set($field, $value);
}
Я привык даже для самых простых таблиц с самой простой логикой писать отдельный класс контейнер. Так проще расширять. В конечном счете большинство ORM-ок так или иначе используют PDO, но добавляют множество своих приятных люшек. Не вижу смысла использовать просто PDO. Если у вас все настолько упирается в производительность, то пожалуйста, пишите на PDO, но это один случай из ста.
xPDO — это не PDO, а его расширение. И оно идет со своими плюшками, и да, для каждой таблицы используются свои объекты, и да, их можно расширять, и они могут расширять друг друга.
Простите, надо выспаться. Я думал x это название драйвера.

И все равно я не вижу явных преимуществ xPDO над Doctrine.
Каждому свое, и свое не каждому.
Дело в том, что xPDO, хоть и самостоятельная библиотека, тем не менее является неотъемлемой частью MODX Revolution. Это по сути их собственная разработка.
Так как MODX — мой основной фреймворк, то логично, что я и буду использовать xPDO.
Я как познакомился с Doctrine 2, так вообще ни в одной ORM не только на PHP, но и на Ruby преимуществ не вижу :)
xPDO в купе с MODX — это не только ORM, это Acl, источники медиа (не только локальная файловая система, но и облака Amazon3), система пакетов, репозитории, офигенная админка и многое-многое другое. Это программный комплекс.

Просто посмотрите вот этот топик для примера: community.modx-cms.ru/blog/documentation/9611.html
Видимо отстал от ModX — когда в последний раз крутил (лет 5 назад, наверное, точно не меньше трех), то Drupal мне показался и мощнее, и проще.
Лет 5 назад еще даже не Evolution, а более ранняя ветка 0.9 была. Это принципиально разного уровня платформы.
Крутил в курах ModX где-то с год назад. Как раз таки эволюшен. Мне не понравилось. Да и в моих проектах она не применима. Разве что для сайтов визиток или других шаблонных сайтов.
Evolution уже как года два не развивается командой MODX (если не считать патчи безопасности).
Revolution — принципиально другая платформа. Могу вас заверить, что я эксперт MODX мирового уровня (не побоюсь таких громких слов) и являюсь MODX Ambassador в Москве.
Разница этих веток настолько принципиальная, что в сообществе даже раскол произошел, и не только в России. Наш community.modx-cms.ru — по большей степени по Revolution.
modx.im (отколовшийся) — по Evolution.

Функционал, который Revolution дает «из коробки» несоизмеримо бОльший по сравнению с Эво. А система пакетов позволяет проекты разрабатывать 100% модульно.

К слову, любой желающий, кто в Москве, может прийти к нам в гости, и я лично отвечу на интересующие вас вопросы и продемонстрирую возможности MODX Revolution.
Пример ужасен. Я по работе использую Symfony и для нее наваяли сервисов/бандлов для интеграци с AWS что не счесть. Не думаю что серьезный проект стоит писать на MODx.
Вот это ужасно?
$id = 2;    // ID медиасурса
// Получаем объект
$source = $modx->getObject('sources.modMediaSource', $id);
// Инициализируем 
$source->initialize(); 
$url = $source->getObjectUrl('images/image.png');

Вот это ужасно:
public function initialize() {
    parent::initialize();
    $properties = $this->getPropertyList();
    if (!defined('AWS_KEY')) {
        define('AWS_KEY',$this->xpdo->getOption('key',$properties,''));
        define('AWS_SECRET_KEY',$this->xpdo->getOption('secret_key',$properties,''));
        /* (Not needed at this time)
        define('AWS_ACCOUNT_ID',$modx->getOption('aws.account_id',$config,''));
        define('AWS_CANONICAL_ID',$modx->getOption('aws.canonical_id',$config,''));
        define('AWS_CANONICAL_NAME',$modx->getOption('aws.canonical_name',$config,''));
        define('AWS_MFA_SERIAL',$modx->getOption('aws.mfa_serial',$config,''));
        define('AWS_CLOUDFRONT_KEYPAIR_ID',$modx->getOption('aws.cloudfront_keypair_id',$config,''));
        define('AWS_CLOUDFRONT_PRIVATE_KEY_PEM',$modx->getOption('aws.cloudfront_private_key_pem',$config,''));
        define('AWS_ENABLE_EXTENSIONS', 'false');*/
    }
    include_once $this->xpdo->getOption('core_path',null,MODX_CORE_PATH).'model/aws/sdk.class.php';

    $this->getDriver();
    $this->setBucket($this->xpdo->getOption('bucket',$properties,''));
    return true;
}


И еще, xpdo включает в себя еще и DI контейнер? Как-то нелогично.

В моих проектах получить путь можно так:
$repository = $this->getDoctrine()->getManager()->getRepository(Acme:Attachment);
// получаем объект
$attachment = $repository->find($id);
// сервис, который отвечает за логику работы с путями
$pathResolver = $this->get('acme.path_resoler');
$uri = $pathResolver->getFileUri($attachment->getUri());


А уже вся логика для работы с файловой системой, использовать AWS или же что-то другое ложится на плечи этого простого сервиса. И настройки все он получает через DI при инициализации.

Подход по сути отличается только реализацией, но не сутью.
ну так и вы бы показали листинги методов getManager(), getRepository() и т.п.
То, что вы процитировали, это часть объекта, обеспечивающего взаимодействие с облаком AmazoneS3. И это внутренности. Программисту же достаточно использования только этого.
$id = 2;    // ID медиасурса
// Получаем объект
$source = $modx->getObject('sources.modMediaSource', $id);
// Инициализируем 
$source->initialize(); 
$url = $source->getObjectUrl('images/image.png');

При этом просто смена $id может в итоге повлиять на то, какой объект-провайдер будет инициализирован, будь то файловая система, AmazoneS3 или какой-то другой свой. И от этого этот код не поменяется. Мы выполним все тот же $url = $source->getObjectUrl('images/image.png');
Получаем обсолютный путь
Это пять!
Мы не на уроке русского языка, и я не филолог.
Это обычная норма русского языка, тем более для специалиста с техническим или физмат образованием.
У меня нет образования, кроме общего среднего.
xPDO — это ORM по паттерну ActiveRecord, если не ошибаюсь.
Ошибаетесь.
PDO (PHP Data Objects) — это php-библиотека (и объект) для взаимодействия с различными базами данными, пришедшая на замену классическим _mysql и т.п. Наверняка же знаете, что методы типа mysql_connect() и т.п. уже устаревшие.

xPDO — это собственная библиотека фреймворка MODX, которая расширяет объект PDO (чисто чтобы обеспечить работу с БД), но сразу имеет в себе и несколько базовых объектов типа xPDOObject, xPDOSimpleObject и т.п.
Плюс весь MODX Revolution построен на этих xPDO-объектах. Тот же пользователь modUser — это в итоге дочерний от xPDOObject.

Все это в купе с отличной админкой, написанной с использованием ExtJS (которая так же славится своей кастомизацией), дает очень мощный инструментарий в руки.
Причем тут админка?

PDO — абстракция над базой.
xPDO — ORM использующая PDO и реализующая паттерн ActiveRecord (о чем к слову даже в документации говорится). Так что не вижу ошибки.
Спасибо за поддержку. А то вроде AR по описанию, а говорят что вообще не ORM 6-/
Нет, я не говорю, что не ORM, но правильно понимать, что это больше чем ORM.
xPDOObject — это же объект с методами set/get, save и т. п.?
Да.
Классическая реализация ActiveRecord — класс абстрактной сущности, реализующей CRUD и наследуемые от него классы сущностей предметной области и приложения.
Индивидуальные акцессоры для полей $comment->setAuthor(new Author('VolCh'). Как вариант — публичные свойства $comment->author = new Author('VolCh')
в нормальной ORM (Doctrine к примеру) генерация схемы идет из структуры ваших сущьностей. Тоесть вы описываете класс, а по нему уже генерятся методы.

Опять же если у вас уже есть таблица, одной командой можно сгенерить мэппинг класс. Геттеры/сеттеры умеют генерить почти все популярные IDE.

Это все же дело вкуса, но поддерживать код, использующий только PDO несколько сложнее.
В xPDO есть методы и генерации объектов из XML-схемы, и XML из описаний объектов, и объекты из структуры БД, и XML из структуры БД и т.п. Все что угодно.
При этом производные от xPDOObject объекты не требуют никаких методов вообще. Есть set(), get(), save(), remove() и этого достаточно. Есть и другие методы, но «этого достаточно».
На выходе мы получаем чистые заготовки, уже способные выполнять основные функции, и при желании можно их расширять как хочется.
И что? Вот примеры с которыми Доктрина мне сильно упрощает жизнь:
— генерация миграций (diff структуры базы по сути производится)
— фильтры
— DQL (частенько бывает полезным)

Словом я вообще не понимаю о чем спор. Та же доктрина (как и большинство других ORM-ок) являются обертками над PDO и расширяют его функционал. Пользоваться или нет решать вам, но в своих проектах я всегда буду использовать ORM-мы, так как они сильно облегчают жизнь. Особенно если в один прекрасный день вы захотите перенести часть данных в MongoDB/PHPCR. Так, все что мне нужно будет сделать это изменить описание мэппинга. Логику же переделывать не придется по большей части.
Вы наверно плохо проследили диалог. Я совершенно не против ORM и т.п. И вы можете работать со своим любимым инструментарием.
Просто я попытался объяснить что такое xPDO, так как многие ошибочно думают, что его назначение — только взаимодействие с различными DB, что совершенно не так.
И я не хотел кого-то убедить пользоваться именно им. Я просто показал его суть.
Вы показали, что xPDO — это ORM или почти ORM.
Это расширение ORM. То есть не только грузовик, но еще и груз имеет.
Не понял метафору.
К тому, что xPDO имеет функционал ORM, но имеет еще дополнительные функциональные объекты.
Как по мне ресолвинг пути файла или его URI/URL никак не должен касаться прослойки работы с базой. А если этот вопрос обойти, все тоже самое можно прикрутить к любой другой ORM, да и готовые реализации есть.

Словом, считаю дальнейшую дискуссию не плодотворной, ибо вы просто описываете особенности MODx.
Взаимно.
А еще зачем-то придумали какие-то статические классы. Не понятно? Ну да и ладно. Каждому своё, и своё не каждому.
Ваш комментарий — отличная иллюстрация, подтверждающая обоснованность опасений, цитирую:

однако многие ведущие разработчики опасались, что типажи будут использоваться не по назначению.
Любая организация логики на основе поведения. Когда добавление сущности в иерархию не дает ничего кроме проблем. Пример из моей практики, отчет по нескольким осям.
Пример: есть репорты с группировкой по магазинам->покупателям->покупкам и по магазинам->продавцам->приобретениям. И это вроде бы укладывается в классическое наследование, но есть еще смешанные репорты, где есть и покупатели и продавцы, и вот тут классическое наследование идет боком.
Пример 2: когда ты в рамках DDD пытаешься абстрагироваться от реализации слоя данных, который для разных репозитариев может быть схожим, а может раздичаться, может использовать разные источники хранения, а то и несколько и реализовывать логику объеденения данных.
Самый простой пример, лично мне, когда не хватало функционала — сделать из класса, у которого есть родитель, обсервер. Вроде и добавить надо 3 метода, но если надо парочку классов изменить?
Чтобы сделать наблюдателя, можно воспользоваться интерфейсами из SPL: SplObserver, SplSubject. И никакого наследования не надо.
Нужно методы этих интерфейсов реализовывать.
А в случае трейтов как? Ведь у каждого наблюдателя своя собственная реализация должна быть.
нет, не своя, все делается по интерфейсам, код общий у всех, просто евенты у каждого свои.
Вот такой пример. Предметная область — транспорт.
class Transport {...}
class RoadTransport extends Transport {...}
class AirTransport extends Transport {...}

А теперь нужно добавить подтип — пассажирский транспорт и грузовой. И пошли плодить подклассы:
PassengerRoadTransport, CargoRoadTransport, PassengerAirTransport, CargoAirTransport.
Либо наоборот — от транспорта наследовать грузовой/пассажирский, и от них уже наследоваться авто/жд/воздушный:

А можно сделать трейты Cargo и Passenger, в которых реализовать специфичные реализации (виртуальных) методов.
class Bus extends RoadTransport {
use Passenger;

}

P.S. Если можно как-то оптимизировать структуру дерева классов без трейтов — подскажите, я что-то не соображу…
Паттерн Мост.
Честно говоря, самое непонятное для меня нововведение в PHP начиная с PHP 3. Как работают разобрался, но вот на практике когда их нужно использовать, а когда нет, так и не понял. Как-то не возникает нужды в множественном наследовании.
Хороший пример использования типажей можно найти во фреймворке Silex. Там есть возможность подключать к приложению модули через них (для удобства).

Сам я тоже еще не столкнулся с задачей где для реализации проекта нужно множественное наследования.
Я столкнулся в текущем проекте с трейтами. Точнее с трейтом и то он носит экспериментальный характер. Используется для подключения ServiceLocator к произвольному классу. Зачем такая странная реализация — без понятия.
Ну как пример, если в Symfony проекте используется множество сервисов, в которые мы через контейнер хотим передавать ObjectManager, лично мне удобнее сделать трейт вида
trait ObjectManager
{
    protected $manager;
    public function setObjectManager (ObjectManager $manager)
    {
        $this->manager = $manager;
    }
    protected function getObjectManager()
    {
        return $this->manager;
    }
}

нежели передавать его через params конструктора.
Ну это просто пример. Таким образом можно включать в классы часто дублируюмую функциональность.
Когда я смотрю на ваш пример, мне приходит в голову еще один:
Можно легко подключать возможность работы с кэшем для любого класса через трейты (методы getCacheKey, getCache, setCache)
Отличная мысль
Честно говоря не очень нравится подобная идея. К такому классу нужна инструкция: «после инстанцирования объекта не забудьте установить свойство такое-то».

Надо поэкспериментировать с определением конструкторов в трэйте. Или, хотя бы, делать setObjectManager() protected/private и вызывать его в конструкторе класса, а в getObjectManager() проверять на null и бросать NPE для забывчивых. Хотя, конечно, от сценария использования класса зависит, но если (почти) всем методам класса менеджер нужен и его инициализация (почти) обязательна, то место этой инициализации в конструкторе.

Конструктор-то вы определить в трейте сможете (как __construct(), так и ClassName()).
Но в этом случае вы:
  1. сможете сделать это только в одном трейте изо всех, используемых в классе и его предках.
  2. не сможете определить конструктор в самом классе.
вот на счет второго утверждения вроде бы не совсем так. Вроде бы приоритет метода объявленного в классе выше чем в типаже. Но я лично не пробовал добавлять конструктор в типаж.
Верно, но в этом случае __construct() из трейта уже не будет конструктором класса и станет всего лишь рядовым методом.
1. insteadof… as… не поможет?
2. Обычные методы трэйтов я точно могу в «наследнике» переопределять, а с помощью insteadof as вызывать и методы «родители» из конкретного трэйта даже при конфликте имн в трейтах.
Все верно говорите, с помощью такого способа можно генерировать прокси-трейты. Правда количество методов в конечном классе будет расти из-за добавления альясов в трейте.
Вот чем меня трэйты и смущают — на сферовакуумных примерах вроде тема интересная, а как пытаешься что-то реализовать, то написать вроде не проблема, но очень трудночитаемый код получается, по-моему. Или просто синдром «коряка не читатель — коряка писатель!»
Видимо я недостаточно ясно выразился.

Конструктор в классе, использующем трейт, молча заместит конструктор из трейта. Конфликт имён конструкторов, определённых в трейтах придётся разрешать явно, через insteadof/as.

Так, что бы определить конструктор в нескольких используемых одновременно трейтах и все они сработали при создании класса, их использующего — не получится.

То, что метод, определённый в трейте, может вообще в принципе выполнять роль конструктора класса для меня выглядит несколько странным.
Вот я хотел поэкспементировать насчёт того получится или нет. То есть через инстид определяем алиасы для конструкторов из трейтов, а в конструкторе класса эти алиасы вызываем.
Получится, но никаких преимуществ от того, что методы будут называться __construct() не будет:

file.php
<?php

  trait Foo {

    function __construct() { echo "Foo::__construct()\n"; }

  }

  trait Bar {

    function __construct() { echo "Bar::__construct()\n"; }

  }

  class FooBarClass {

    use Foo, Bar {
      Foo::__construct as initFoo;
      Bar::__construct as initBar;
    }

    public function __construct() {
      echo "FooBarClass::__construct() {\n";
      $this->initFoo();
      $this->initBar();
      echo "}\n";
    }

  }

  $objFooBar = new FooBarClass();

?>


output
X:\>php.exe file.php
FooBarClass::__construct() {
Foo::__construct()
Bar::__construct()
}


С тем же успехом мы могли их изначально назвать initFoo() и initBar().
Если это применять к сеттерам, то DI умеет после инициализации сэтить значения. Это зачастую очень удобно. Так что трейты тут очень даже подходят.
Да, именно это я и имел в виду, а в описании сервиса делать
services:
    MyService:
        class: NS\MyBundle\Service\MyService
        calls:
          - [ setObjectManager,   [ @doctrine.orm.entity_manager ] ]
Не надо никакой инструкции. В случае Symfony 2, просто прописывает зависимость:
services:
    MyService:
        class: NS\MyBundle\Service\MyService
        calls:
          - [ setObjectManager,   [ @doctrine.orm.entity_manager ] ]
Мы у себя трейты исполуем во всяких DTO и еще примерно так gist.github.com/1034079

Хочу добавить, что трейты — это не про наследование. Это про горизонтальное расширение.
По второмц пример. Я правильно понял, что вы из DataMapper делаете ActiveRecord?
не, я бы так делать не стал, но SerializableEntity, Timestampable вполне себе =)
Вот это, да, нормальная идея. Дальнейшее развитие интерфейов типа *able. Довольно часто в *able классах приходится писать реализации таких интерфейсов мало чем отличающиеся друг от друга. Правда, нужно будет организовывать какие-то колбэки, которые обязательно должны бы в типажируемом классе или использовать рефлексию, ведь типаж ничего о классе не знает. В общем к типажу нужна инструкция по использованию, соглашения, которым должен отвечать типажируемый класс.
Phython'ом мой любимый язык ещё никто не обзывал…
так было в оригинале, я решил сохранить стилистику)
Согласен с распространённым мнением, что это теоретически не столько множественное наследование, сколько умный копипаст.
Другое дело, что на практике в значительном количестве случаев множественное наследование используют именно для копипаста.
Лучше бы декораторы сделали…
Поддерживаю. Очень хотел бы видеть в PHP декораторы похожие на те которые есть в питоне
У меня есть реализция PHP декоратора — можешь глянуть в профайле ссылку на статью.
В Вашей реализации есть достаточно заметные недостатки, а именно необходимость изменять исходный код декорируемых классов (use TDecorator;).
Такая реализация бесполезна в случае использования сторонних библиотек.

Советую всеже посмотреть как декораторы реализуются в питоне — там это сделано очень красиво за счет метапрограммирования и переопределения функций/методов в рантайме. Именно о такой реализации я и мечтаю.
Тоже самое будет и в Python: невозможно без изменения кода сторонней библиотеки декорировать метод класса.
Совсем не тоже самое.
Допустим у нас есть декоратор:
def decorate(func):
	def world(*args, **kwargs):
		func(*args, **kwargs)
		print "World"
	return world


Тут возможны 2 случая:
1) Вы хотите добавить этот функционал в код всех обьектов некого класса
class Decorated:
	@decorate
	def speak(self):
		print "Hello"

теперь все вызовы метода будут проходить через декоратор

2) Добавить функционал к одному из обьектов данного класса

test = Decorated()
test.speak = decorate(test.speak)
test.speak()

И в этом случае вам не надо изменять код самого класса.
То есть в Вашем понимании вот это:
class Decorated:
    @decorate
    def speak(self):
        print "Hello"

не изменить исходный код класса?
Я специально привел 2 варианта: с изменением и без. Во 2-м варианте исходный код класса не изменяется:
class Decorated:
    def speak(self):
        print "Hello"

test = Decorated()
test.speak = decorate(test.speak)
test.speak()

Прошу прощения если в первый раз привел недостаточно подробный пример
Метапрограммирование и переопределение методов в рантайме уже есть ) Без экстеншенов, без магии, с очень низким оверхедом на вызов перехваченого метода. Библиотека Go! AOP к вашим услугам. Только ей под силу добавить к любому вашему классу нужный трейт, не меняя исходный код оригинальных классов.
Пример: берем некоторый класс Example (в нем нет трейтов и интерфейсов), дальше нам захотелось иметь возможность сериализовать объекты данного класса, не меняя исходный код класса. Все просто — создаем трейт SerializableImpl и объявляем совет DeclareParents с интерфейсом Serializable и трейтом SerializableImpl в отношении класса Example, после чего наш класс Example будет имплементировать интерфейс Serializable, в чем можно убедиться проверкой instanceof.
Лучше бы нативную поддержку юникода сделали…
Так вроде с 5.4 она есть?
Нет, всё примерно так же как и раньше — mb_* функциями. А нативную поддержку обещали в 6.0, который неизвестно когда выйдет. тоже очень жду.
Угу, и это в 2013 году в языке, который HyperText Preprocessor. В Java полная поддержка unicode была уже в 1998 году.
Сдается мне, в том то и проблема, что изначально на юникод не ориентировались, и сейчас приходится переписывать тонны кода и ничего при этом не сломать.
Ну Python, Ruby как-то смогли. Начинали с того же примерно, что и в PHP в этом плане.
НЛО прилетело и опубликовало эту надпись здесь
Python 3 не используется

Спорное утверждение.
python3wos.appspot.com/
Процесс идет, медленно но верно.
НЛО прилетело и опубликовало эту надпись здесь
А при чем тут Python 3? Даже во втором поддержка юникода на все еще недостижимом для php уровне.
6.0 не будет. Юникод не получился. До скончания веков mb_ вызывать.
Решение не из лучших, но можно юзать mbstring.func_overload.
Это очень плохое решение, никогда так не делайте. Огромное количество php-библиотек предполагает, что строковые функции оперируют байтами (например, strlen често используется для получения именно размера (двоичной) строки в байтах). Оверлоад эти библиотеки ломает, причём в самых неожиданных местах.
Спасибо, хорошо. Я всегда догадывался, что это еще тот костыль, и сам никогда его не использовал.
вот ни разу не попадались подобные библиотеки. Я так понимаю это относится к случаям, когда нужно что-то в поток отправить (аля структуру какую-то), но не уверен. Можете привести пример?
а вот я столкнулся с проблемой кодировок тут
sphinxsearch.com/forum/view.html?id=1136
sphinxsearch.com/forum/view.html?id=530
This is interesting enough so I'll make a translation for those who do not speak Russian
as well.

David points out that mbstring.func_overload, which enables standard PHP string functions
to work OK with UTF-8, might be causing troubles with sphinxapi.php which uses strlen()
when forming request.

He also suggests a fix to sphinxapi.php which would check that and temporarily switch the
encoding to latin1. (which I suppose fixes strlen())
Ну да, я и есть тот Давид:) Ещё я с этим сталкивался в PHPExcel, PHP Markdown, PHPMailer… в общем, сто раз уже пожалел, что принял когда-то решение использовать оверлоад в проекте. Приучить себя писать везде mb_* гораздо проще, чем разруливать такие вот глюки.
НЛО прилетело и опубликовало эту надпись здесь
Полное перепроектирование слишком много времени займет. Да и я не припомню, когда у меня в последний раз на проектах были проблемы с обработкой юникода. Я больше жду, когда парсер PHP файлов перепишут, что бы на выходе он генерил не опкоды сразу, а сначала AST.
Еще из интересных применений трейтов — динамическое их подключение к конечным классам с помощью аспектно-ориентированного программирования. Я как раз недавно сделал поддержку технологии внедрения (Introduction) трейтов в любой класс, а также добавил уникальную возможность перехвата методов в трейтах с помощью генерации прокси-трейтов.

Так что трейты — это хорошо, в сочетании с интерфейсами. А декораторы уже есть благодаря АОП.
А в чем смысл? Ну тоесть, я не могу представить ситуации, когда может понадобится возможность динамически менять классы приложения. Использовать как плагины? Вместо декораторов? Да и меня немного смущает факт того, что в последнее время все больше и больше людей пытаются решить какие-то проблемы при помощи АОП. Может быть для сервисных задачь оно и подходит (профилирование кода, логирование), но все же я против этого подхода. Хотя это опять же лично мое мнение.
Я уже в своей статье на хабре приводил пример АОП, когда достаточно перед методом добавить аннотацию @Cacheable — и он будет кэшироваться, неважно какой метод — динамический, статический, или же вообще в трейте. Это пример номер раз.
Пример номер два (про него еще будет отдельная статья на хабре): есть у вас тонна сущностей доктрины, а вам хочется отдавать их как-то красиво в JSON и начинается вызов тучи геттеров, либо гидрация, а можно сделать это очень элегантно — ко всем классам-сущностям добавить интерфейс JsonSerializable и трейт с реализацией этого интерфейса. Профит. После этого можно любую сущность заворачивать в REST-интерфейс одним контроллером.
Из реальных примеров — для админки SonataBundle одним классом добавляем журналирование всех действий в админке. Неплохо, да, не меняя оригинальный код?
В одном из проектов так же реализовывал кеширование сабреквестов для отображения виджетов. Но там все же не AOP а события. Тоесть у вас есть событие onRequest где мы проверям закешили мы уже или нет? Если закэшили то просто отдаем ответ из кэша. И событие onResponse, где мы забираем ответ и смотрим, нужно ли его кешировать.
Использование событий влечет за собой написание шаблонного кода, поэтому вы не сможете расширить логику любого метода, если он заранее не написан с учетом возможного расширения. Да и обработка событий добавляет очень большой оверхед. Так что у меня в библиотеке нет никаких событий, хотя если сравнивать по смыслу — то да, в какой-то мере это событие. Только можно создать его в любом месте кода без предварительной подготовки )
I also fear that it may fall into the category of often-abused-features such as eval(), goto, constants, the @ operator, class inheritance and regular expressions.


Как можно злоупотреблять константами?
При разработке нового проекта возникла необходимость множественного наследования, ну или множественного использования части кода, я очень рад что на этот момент примеси ( трейты) уже были реализованны.
Вот пример: Описывая модели, я столкнулся с тем, что ряд моделей имеют общий код, но в радителя его вставить нет возможности, объясню почему. Модели описывались визуальных объектов, с дальнейшим общением с JavaScript. Была описанна базовая модель Компонент, в котовую вынесена вся общая часть визуальных компонентов (координаты, размеры, ...). Далее описывались сами компаненты, для которых был создан ряд общих родителей (общий для кнопок, у которых было событие нажатия, общий для компонентов, которые могут иметь дочерние элементы, ...) Проблымы появились тогда, когда я добрался до компонентов Изображение и ИзображениеКнопка. По сути ИзображениеКнопка дочерняя сущность от сущности Кнопка, т.к. есть обработчик нажатия, но в ней есть еще описание и весь функционально компонента Изображение, т.к. полностью повторяет функционально и Кнопки и Изображения! Тут я и воспользовался возможностями примесей. Скажу Вам что пхпШторм очень хорошо умеет с ними работать и я еще не пожалел что воспользовался ими. Для большей убедительности, стоит сказать что подобных компонентов у меня несколько, которые содержат полное или частичное дублирование.

Пишу с телефона, прошу прощения за ошибки и опечатки.
Проблымы появились тогда, когда я добрался до компонентов Изображение и ИзображениеКнопка. По сути ИзображениеКнопка дочерняя сущность от сущности Кнопка, т.к. есть обработчик нажатия, но в ней есть еще описание и весь функционально компонента Изображение, т.к. полностью повторяет функционально и Кнопки и Изображения! Тут я и воспользовался возможностями примесей.
IMHO композиция кнопки и изображения выглядело бы здесь более логичным решением.
Действительно, несколько странно, что типажи получают возможность иметь состояние, что довольно не верно по идеологии паттерна. То есть, получается, что это что-то соеднее между примесями (mixins) и собственно трейтами.

Боюсь, такая путаница не пойдет на пользу архитектуре, тем более, что примеси дают больше возможностей запутать логику.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории