Реализация enum на php

  • Автор темы Автор темы stalxed
  • Дата начала Дата начала

stalxed

Новичок
Замечаю в коде Java и C# что enum прикольная штука.
В php обычно для этой цели использую константы классов. Т.е. проверка инвариантов ложиться на родительский класс, что мне не нравится. Т.е. setGroup($group) в классе User должен проверять, что группа существует. setGroup(GroupEnum $group) кажется куда логичнее(проверку инвариантов осуществляет сам GroupEnum).

Достойные реализации нашёл тут:
http://php.net/manual/en/class.splenum.php
https://github.com/myclabs/php-enum

Конструктор прост, принимает один аргумент, и кидает эксепшен, если значение не существует.
Какие я вижу проблемы:
1) Сравнение со значением:
Тут несколько вариантов:
  • $group->is(GroupEnum::Admin);
  • $group == GroupEnum::Admin;
  • GroupEnum::Admin == $group;
2 и 3 я даже не уверен, какое из них будет работать... Судя по коду php-enum 2 вариант работает, а вот 3 будет ли симметрично работать ли.. не уверен. Короче игра динамической типизации. А первый не очень то красив.
2) Сравнение двух enumов
Тоже несколько вариантов:
  • $group->isEqual($anotherGroup);
  • GroupEnum::compare($group, $anotherGroup);
тоже выглядит ужас...
хотя в теории наверное должно работать:
  • $group == $anotherGroup
Теория гласит, что должен быть одинаковый класс, и равны все члены классов. Наверное должно работать, но опять магия...

Хотелось бы узнать, есть ли у кого реально достойные примеры реализации enum?
 
PHP:
in_array($group, array('anonymus','guest','user','admin'))
Это просто проверка, а я о типе enum.

Что-то вроде:
PHP:
class GroupEnum
{
    const ANONYMUS = 'anonymus';
    const GUEST = 'guest';
    const USER = 'user';
    const ADMIN = 'admin';

    private $current;

    public function __construct($current)
    {
        if (! in_array($current, $this->getAll())) {
            throw new \InvalidArgumentException();
        }

        $this->current = $current;
    }

    public function getAll()
    {
        return [
            self::ANONYMUS,
            self::GUEST,
            self::USER,
            self::ADMIN
        ];
    }

    public function __toString()
    {
        return $this->value();
    }

    public function value()
    {
        return $this->current;
    }

    public function isEqual(GroupEnum $another)
    {
        return self::compare($this, $another);
    }

    public static function compare(GroupEnum $first, GroupEnum $second)
    {
        return ($first->value() == $second->value());
    }
}

К моему удивлению, вся возможная магия работает:
PHP:
$test = new GroupEnum(GroupEnum::ADMIN);
$another = new GroupEnum(GroupEnum::ADMIN);
echo ($test == GroupEnum::ADMIN) ? '+' : '-';
echo (GroupEnum::ADMIN == $test) ? '+' : '-';
echo ($test->isEqual($another)) ? '+' : '-';
echo (GroupEnum::compare($test, $another)) ? '+' : '-';
echo ($test == $another) ? '+' : '-';
Выводятся одни плюсы.

Просто в 2015 году немного странно изобретать ENUM...
И магия здесь тоже...

Просто сторонники ООП уже имеют наверное в закромах отточенную реализацию ENUM, надеюсь что кто-нибудь поделится.
 
Просто до 2015го года никому в голову не пришло изобретать этот велосипед :D
 
попрос зачем мне константы групп, если в базе в таблице групп у меня strings и группы динамично наполняются?
 
попрос зачем мне константы групп, если в базе в таблице групп у меня strings и группы динамично наполняются?
Не обязательно string, если группы заданы статично и их не нужно менять в админке.
А преобразовывать в SQL enum не проблема, в doctrine это выглядит так:

PHP:
class GroupEnumType extends Type
{
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        $values = '\'' . implode('\', \'', GroupEnum::getAll()) . '\'';

        return sprintf('ENUM(%s) COMMENT \'(DC2Type:%s)\'', $values, $this->getName());
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return new GroupEnum($value);
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value->value();
    }

    public function getName()
    {
        return 'GroupEnum';
    }
}
 
когда уже готово динамично, зачем делать статично?
Этот пример является примером.

У меня в проекте сейчас до 10 случаев есть, в которых я пользуюсь просто константами, которые заданы в родительском классе, относительно enum переменной.
Код был бы значительно читабельнее, если использовать enum...
 
Выдумывать примеры, чтобы потом на их примере создавать себе сложности - дело нехитрое. И бабла под это дело тоже можно отжать нехило.
 
Код был бы значительно читабельнее, если использовать enum...
поставил галочку, есть права, убрал, нет прав. вообще interface это по своему enum подходят только те классы/обьекты которые этим типом являются, хоть даже interface пустой.

ну а так юзай константы, на здоровье
 
Просто до 2015го года никому в голову не пришло изобретать этот велосипед :D
у меня есть такой же велосипед с почти идентичной реализацией как у https://github.com/myclabs/php-enum/blob/master/src/Enum.php

вот, создал gist
https://gist.github.com/Redjik/52a801da35d7d2fe9108
все руки не дойдут доделать и тестами покрыть ;)

ЗЫ. с помощюсь serialize можно напрямую с доктриной работать
 
Последнее редактирование:
вот, создал gist
https://gist.github.com/Redjik/52a801da35d7d2fe9108
все руки не дойдут доделать и тестами покрыть ;)
Спасибо за код. Магии много :)
Думаю в своей реализации такое исключить GroupEnum::ADMIN();
так как new GroupEnum(GroupEnum::ADMIN) рефакторинг средства правят автоматом(название константы).

ЗЫ. с помощюсь serialize можно напрямую с доктриной работать
В доктрине удобнее использовать enum(т.е. чтобы в БД был енум), код выше(сообщение 6 в этой теме).
Минус, что для каждого конкретного типа enum такой класс создавать. Но там всего 10 строчек кода.
Поиск работает с таким. Да и если руками понадобится в БД лесть - тоже удобнее.

Выдумывать примеры, чтобы потом на их примере создавать себе сложности - дело нехитрое. И бабла под это дело тоже можно отжать нехило.
В каждом проекте есть enum типы.

поставил галочку, есть права, убрал, нет прав. вообще interface это по своему enum подходят только те классы/обьекты которые этим типом являются, хоть даже interface пустой.

ну а так юзай константы, на здоровье
Интерфейсы не подойдут, а вот классы кстати можно, пустые классы:
interface EnumGroupInterface{}
class AdminEnumGroup implements EnumGroupInterface {}
Но имхо - не удобно...

Минус констант - нужна в нескольких местах валидация списка значений! В НЕСКОЛЬКИХ! И порой эта валидация теряется.
 
Сверху