• Как разделить код ViewModel-ей на каждое окно?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Я правильно понял, что есть несколько моделей (полученных из сервиса) и есть вьюмодель для каждой модели. И в какой-то момент происходит выбор показа нужной модели, это уже есть. Я правильно понял?

    Я бы попробовал в MainViewModel сделать список ViewModel-ей. Если добавляется новая модель, то добавлять в список. В MainViewModel добавить CurrentViewModel, имеющий ссылку на текущую вьюмодель, которая в данный момент нужно выводить. И в MainViewModel сделать метод, которыйпоказывает нужную вьюмодель по выбранной модели (что-то типа private ViewModel SelectModel(Model model) {}).
    Ответ написан
    Комментировать
  • Как сравнить два списка?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Сначала определите, какие элементы совпадают в отдельный список (с помощью Intersect). А потом в цикле проверяйте, есть ли каждый элемент требуемого списка в этот проверочный список (с помощью Contains).
    Ответ написан
    Комментировать
  • Можно ли указать в одном биндинге несколько конвертеров?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам не нужно делать второй конвертер, а настроить тот, чтобы возвращал нужное значение - возвращать Visibility.Collapsed при истине и Visibility.Visible при лжи.
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((bool)value)
        {
            return parameter != null ? Visibility.Collapsed : Visibility.Hidden;
        }
        return Visibility.Visible;
    }


    А если нужно именно проверять несколько различных проверок у одного элемента, то используйте MultiBinding. Я написал об этом тут.
    Ответ написан
    9 комментариев
  • Как в xaml делать несколько конвертеров?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    В вашем случае, подойдёт создание специального кастомного конвертера, который сделает и из числа в строку, и потом строку обработает.

    Другой вариант - MultiBinding. Создаётся специальный мультиконвертер, в который указывается несколько обычных биндингов. Но они обрабатываются параллельно, поэтому, если нужно вначале делать первый конвертер, а потом использовать его результат в другом конвертере, то так не получится, все данные обрабатываются в мультиконвертере.

    Но мультибиндинг работает в WPF, а в Silverlight нет. Но для Silverlight есть workaround: www.codeproject.com/Articles/286171/MultiBinding-i...

    Вот пример мультиконвертера, который берёт несколько bool, и проверяет, все ли они истинны (или ложны - можно выбрать).

    Использование конвертера:
    xmlns:converters="clr-namespace:MyProject.Converters"
    
    <CheckBox x:Name="Checkbox1" Content="Чекбокс 1"/>
    <CheckBox x:Name="Checkbox2" Content="Чекбокс 2"/>
    <ToggleButton Content="Кнопка">
        <ToggleButton.IsEnabled>
            <MultiBinding Converter="{converters:BooleanMultiConverter Type=AllTrue}">
                <Binding Path="IsChecked" ElementName="Checkbox1"/>
                <Binding Path="IsChecked" ElementName="Checkbox2"/>
            </MultiBinding>
        </ToggleButton.IsEnabled>
    </ToggleButton>

    Сам конвертер:
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Markup;
    
    namespace MyProject.Converters
    {
        public class BooleanMultiConverter : MarkupExtension, IMultiValueConverter
        {
            /// <summary>
            /// Перечисление типа конвертера
            /// </summary>
            public enum Types
            {
                AllTrue,
                AllFalse,
                AnyTrue,
                AnyFalse,
            }
    
            /// <summary>
            /// Тип конвертера, по умолчанию - AllTrue
            /// </summary>
            public Types Type { get; set; }
    
            public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                switch (Type)
                {
                    case Types.AllTrue:
                        return values.All(v => (bool)v);
                    case Types.AllFalse:
                        return values.All(v => !(bool)v);
                    case Types.AnyTrue:
                        return values.Any(v => (bool)v);
                    case Types.AnyFalse:
                        return values.Any(v => !(bool)v);
                    default:
                        throw new InvalidOperationException();
                }
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
            {
                return new[] { DependencyProperty.UnsetValue };
            }
    
            #region MarkupExtension
    
            public BooleanMultiConverter()
            {
            }
    
            /// <summary>
            /// Отображать конвертер в списке
            /// </summary>
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                return this;
            }
    
            #endregion
        }
    }

    MarkupExtension используется, чтобы не создавать кучу конвертеров в ресурсах
    Ответ написан
    2 комментария
  • Двойной клик по итему ListBox-а?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно просто поставить привязку с командой у вашей вьюмодели. Вы ведь используете MVVM?

    Гораздо удобнее использовать не создавать вручную ICommand, а сделать класс, реализующий этот интерфейс. Обычно его называют DelegateCommand (классы указаны ниже).

    И дальше во вьюмодели создаёте свойство для команды и привязывайте то место, где нужна команда, к созданной команде во вьюмодели.

    #region Свойства
    
    public ObservableCollection<MyClass> Collection { get; set; } = new ObservableCollection<MyClass>
    {
        new MyClass { Name = "Иванов Иван" },
        new MyClass { Name = "Петров Пётр" },
        new MyClass { Name = "Сидоров Сидор" },
    };
    public MyClass SelectedCollection { get; set; };
    
    #endregion
    
    #region Команды
    
    private ActionCommand _myCommand;
    public ActionCommand MyCommand => _myCommand ?? (_myCommand = new ActionCommand(MyMethod));
    
    private DelegateCommand<MyClass> _myDelegateCommand;
    public DelegateCommand<MyClass> MyDelegateCommand => _myDelegateCommand ?? (_myDelegateCommand = new DelegateCommand<MyClass>(MyMethod2, item => item != null);
    
    private void MyMethod()
    {
        // обработка команды
    }
    
    private void MyMethod2(MyClass item)
    {
        // обработка команды
    }
    
    #endregion


    <ListBox ItemsSource="{Binding Collection}" SelectedItem="{Binding SelectedItem}"/>
    <Button Command="{Binding MyCommand}" Content="Команда 1"/>
    <Button Command="{Binding MyDelegateCommand}" CommandParameter="{Binding SelectedItem}" Content="Команда 2"/>


    Если нужна команда не на событие Сlick, а на любое другое событие (как вы говорите, на DoubleClick), то нужно сделать добавить референс на System.Windows.Interactivity и указать триггер на событие:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    <ListBox ItemsSource="{Binding Collection}" SelectedItem="{Binding SelectedItemc}" DisplayMemberPath="Name">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <i:InvokeCommandAction Command="{Binding MyDelegateCommand}" CommandParameter="{Binding SelectedItem}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>


    Класс DelegateCommand - команда с параметром, и класс ActionCommand - команда без параметра. Нешаблонный класс DelegateCommand используется реже (я вообще не припомню, чтобы его использовал).
    public class DelegateCommand<T> : ICommand
    {
        #region Private fields
    
        private readonly Action<T> _execute;
        private readonly Func<T, bool> _canExecute;
    
        #endregion
    
        #region Constructors
    
        public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        #endregion
    
        #region DelegateCommand
    
        public void Execute(T parameter)
        {
            var handler = _execute;
            if (handler != null)
            {
                handler(parameter);
            }
        }
    
        public bool CanExecute(T parameter)
        {
            var handler = _canExecute;
            return handler == null || handler(parameter);
        }
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        #endregion
    
        #region ICommand
    
        void ICommand.Execute(object parameter)
        {
            Execute((T)parameter);
        }
    
        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute((T)parameter);
        }
    
        #endregion
    }
    
    public class DelegateCommand : DelegateCommand<object>
    {
        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
            : base(execute, canExecute)
        {
        }
    }
    
    public class ActionCommand : DelegateCommand<object>, ICommand
    {
        #region Private fields
    
        private readonly Action _action;
        private readonly Func<bool> _canExecute;
    
        #endregion
    
        #region Constructors
    
        public ActionCommand(Action action, Func<bool> canExecute = null)
            : base(null, null)
        {
            _action = action;
            _canExecute = canExecute;
        }
    
        #endregion
    
        #region ActionCommand
    
        public void Execute()
        {
            var handler = _action;
            if (handler != null)
            {
                handler();
            }
        }
    
        public bool CanExecute()
        {
            var handler = _canExecute;
            return handler == null || handler();
        }
    
        #endregion
    
        #region ICommand
    
        void ICommand.Execute(object parameter)
        {
            Execute();
        }
    
        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute();
        }
    
        #endregion
    }
    Ответ написан
    1 комментарий
  • Какое разрешений экрана выбрать на Unity3d для Android игрушки?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Ответ написан
    Комментировать
  • Как ограничить расширение?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Это сделать не получится, к сожалению.
    Предлагаю сделать несколько подобных методов в одном классе, по одного для каждого требуемого типа:
    public class NumericExtensions
    {
      public static Int32 ToInt32(this Double line)
      {
        return (Int32)line;
      }
      public static Int32 ToInt32(this float line)
      {
        return (Int32)line;
      }
      public static Int32 ToInt32(this String line)
      {
        var res;
        int.TryParse(line, out res);
        return res;
      }
      // ... для каждого нужного типа
    }
    Ответ написан
    2 комментария
  • Почему не работает Binding?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Наверху {Binding Path=Dat1}, внизу {Binding Path=Dt1}. Опечатка - не хватает символа a.

    При запуске программы в студии в окне Output часто выводятся диагностические сообщения, и подобные ошибки там выводятся.
    Ответ написан
    7 комментариев
  • Можно ли использовать EntityFramework только в Class Library?

    lexxpavlov
    @lexxpavlov Автор вопроса
    Программист, преподаватель
    Решение описано в статье. Настройки БД хранятся в коде, в файле App.config библиотеки настроек EF писать ничего не нужно.

    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Data.Entity.SqlServer;
    using Models;
    
    namespace DatabaseLibrary
    {
        public class MyConfiguration : DbConfiguration
        {
            public MyConfiguration()
            {
                SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
                SetDefaultConnectionFactory(new LocalDbConnectionFactory("v11.0"));
            }
        }
    
        [DbConfigurationType(typeof(MyConfiguration))]
        public class Database : DbContext
        {
            private const string ConnectionString = @"мой connection string";
    
            public Database() : base(ConnectionString)
            {            
            }
    
            // мои DbSet-ы
        }
    }

    Остаётся решить, как в WPF-проекте использовать данные в Design-режиме.
    Ответ написан
  • Разработка игр с умом?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Рекомендую начать с чего-нибудь попроще, пусть даже клон. Сделайте клон какого-нибудь платформера или Galaxy (или что больше нравится). Это важно, потому что дальше будет слишком много времени будет уходить на сложные задачи по программированию, с которым у вас пока слабо. Когда вы сделаете вашу первую программу, дальше вам будет гораздо проще. Если вы будете хорошо развиваться, то можно попробовать сделать за 2-4 недели, и только потом идти к вашей игре, которую вы хотите делать. Получив какой-то опыт, вам будет легче понять хорошим советам по архитектуре игры.
    Лично у меня есть несколько человек, с которыми я общаюсь и помогаю с программированием. Напишите в скайп, можно начать общение.
    Ответ написан
    7 комментариев
  • Что делают делегаты в C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вашу задачу можно сделать тремя способами:
    1) создать метод, который будет суммировать каждый элемент списка в переменную, точнее, в поле класса.
    long Result;
    long Sum(List<int> intList)
    {
        Result = 0;
        intList.ForEach(AddElement);
        return Result;
    }
    
    void AddElement(int i)
    {
        Result += i;
    }

    2) Чтобы не создавать "лишнюю" функцию, необходимую только для конкретной задачи, то можно взять анонимную функцию. Анонимные функции в C# используются с помощью делегатов. Дополнительно, анонимные функции могут использовать замыкания, и нет необходимости создавать внешнее поле.
    long Sum(List<int> intList)
    {
        long result = 0;
        intList.ForEach(delegate(int i) { result += i; });
        return result;
    }

    3) Можно взять лямбду - в данном случае, это "синтаксический сахар" - упрощение создания делегата.
    long Sum(List<int> intList)
    {
        long result = 0;
        intList.ForEach(i => result += i);
        return result;
    }
    Ответ написан
    2 комментария
  • C# использование оператора || - что я делают не так?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Надо использовать И, а не ИЛИ, а у вас получилось "Число не двойка ИЛИ не тройка", то есть всегда истина.
    Ответ написан
    Комментировать
  • C# Троелсен и Шилд, поворот не туда?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Троелсен пишет профессионалам, тем, кто уже профессиональный разработчик в каком-либо языке, а теперь хочет разобраться с C#. Раз книга направлена на профессионала, то в ней не указываются общие основы программирования - например, нет ничего про паттерны, нет инфы, для чего нужен ООП, какие типы данных выбрать для конкретной задачи, какие типы массивов/списков взять, и т.п. Например, в книге описаны три разных способа работы с потоками. Каждый из типов хорошо описаны, но не указаны, какой из способов лучше использоваться. (хотя, конечно же, автор пытался показать, для чего нужны эти типы и в чем они отличаются, но выбор конкретного типа работы с потоками нужно выбирать разработчику.)
    Например, если разработчик хорошо знает Java. Тогда для него книга Троелсена идеальна. Можно понять, что в языке есть и как этим пользоваться.
    В итоге, эта очень сильная книга, которую легко можно использовать как справочник.
    Поэтому, эту книгу я не рекомендую как учебник программирования. Но когда вы пару-тройку лет поработаете с шарпом, то эта книга даст полное развёрнутое описание языка.

    P.S. Шилдта я не читал, не могу рассказать, на что она направлена.
    Ответ написан
    4 комментария
  • При выполнении обрезаются края окна в WPF. В чем тут проблема?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Не могу у себя проверить, у меня всё видно хорошо (у меня Win7).

    Попробуйте убрать все настройки внешнего вида. Будет ли нормально видно?
    <StackPanel>
        <Label Visibility="Collapsed" Content="Неверные данные авторизации" HorizontalAlignment="Center" Margin="10,7,10,0" Width="Auto" Foreground="Red"/>
        <Label Content="Логин:"/>
        <TextBox x:Name="loginTxt" TextWrapping="NoWrap"/>
        <Label Content="Пароль:"/>
        <PasswordBox x:Name="passwordTxt"/>
        <Button Content="Войти"/>
    </StackPanel>

    Если не изменится, то уберите атрибуты в Window.
    Если заработало, то постепенно возвращайте атрибуты в элементы. Или установите общий стиль для всех элементов сразу, чтобы для всех элементов стали одинаковые стили:
    <Window.Resources>
        <Style x:Key="CommonStyle" TargetType="Control">
            <Setter Property="Margin" Value="5" />
            <Setter Property="Padding" Value="5" />
            <Setter Property="FontSize" Value="16"/>
            <Setter Property="Width" Value="150"/>
        </Style>
        <Style TargetType="Label" BasedOn="{StaticResource CommonStyle}"/>
        <Style TargetType="TextBox" BasedOn="{StaticResource CommonStyle}"/>
        <Style TargetType="PasswordBox" BasedOn="{StaticResource CommonStyle}"/>
        <Style TargetType="Button" BasedOn="{StaticResource CommonStyle}"/>
    </Window.Resources>
    Ответ написан
    3 комментария
  • C#. Операторы доступа для сокрытия, но не для защиты?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Инкапсуляция нужна не для защиты самого кода (вы правильно сказали - всё всегда можно увидеть), а для того, чтобы защитить код, использующий класс, от изменений этого класса. Есть такие два понятия - интерфейс и реализация. У любого класса должно быть назначение (и желательно - только одно назначение), и те элементы, относящиеся к назначению, делают публичным. А те элементы, которые не связаны с самой задачей и предназначены для вспомогательных функций, то это закрытые элементы, пользователю этого класса они не нужны.

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

    Как пример, пусть будет такой класс для решения квадратного уравнения:
    class KvadrUr
    {
        public double A { get; set; }
        public double B { get; set; }
        public double C { get; set; }
    
        public bool HasRoots { get { return GetDiscr() >= 0; } }
    
        public KvadrUr()
        {
        }
    
        public KvadrUr(double a, double b, double c)
        {
            A = a;
            B = b;
            C = c;
        }
    
        public double[] GetRoots()
        {
            var discr = GetDiscr();
            if (discr < 0) return new[] {double.NaN, double.NaN};
            var sqrt = Math.Sqrt(discr);
            return new[]
            {
                (-B + sqrt) / (2 * A), 
                (-B - sqrt) / (2 * A)
            };
        }
    
        private double GetDiscr()
        {
            return B*B - 4*A*C;
        }
    }

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

    А теперь представьте, что получение дискриминанта будет публичным методом. Тогда кто-нибудь возьмёт и начнёт использовать этот метод в своём коде. И если автор класса захочет и изменит реализацию на другой способ (по формуле Виета, например), то дискриминант уже будет не нужен, и метод GetDiscr() нужно будет удалить. Но в том месте, где его использовали, будет ошибка - метод GetDiscr() не существует.

    Таким образом, если в классе есть точное указание, что является интерфейсом, а что реализацией, даёт коду больше возможности на изменение этого кода.

    Во многих языках программирования имеется особенная возможность, связанная с интерфейсами. В C# это одноимённое понятие - interface. В данном примере хорошо бы создать такой интерфейс:
    interface IRoots
    {
        bool HasRoots { get; }
        double[] GetRoots();
    }
    class KvadrUr : IRoots
    {
        // ...
    }

    Можно создать несколько разных классов - один считает через дискриминант, другой - через формулу Виета, третий - через выделение полного квадрата, четвёртый - кубическое уравнение, пятый - биквадратное уравнение. Но каждый из классов реализуют один и тот же интерфейс, и каждый из них может быть взаимозаменяем.
    Ответ написан
    2 комментария
  • Как лучше организовать структуру таблиц?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вот почитайте мою статью, в ней как раз рассказывается о вашей проблеме:
    Реализация системы тегов в админке с бандлом Sonat...
    Ответ написан
    Комментировать
  • С#. Почему нужно прописывать using System.ChildNamespace, когда уже указано using System?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Часто вообще можно не писать using, а классы всегда указывать полностью:
    System.Int32 i = 10;
    System.Console.WriteLine("Строка");
    System.Collections.Generic.List<int> list = new System.Collections.Generic.List<int>();

    Но если указать using-и, то можно писать меньше (а значит, почти всегда лучше):
    Int32 i = 10;
    Console.WriteLine("Строка");
    List<int> list = new List<int>();

    Если в одном файле используются два разных класса с одинаковым именем, то в этом случае можно написать один из классов через using, а второй - написать полное имя. А можно создать псевдоним класса:
    using IntList = System.Collections.Generic.List<int>;
    
    IntList lst = new IntList();
    List<double> lst2 = new List<double>();

    В общем, using-и используются для удобства - меньше писать.

    P.S. Если использовать Resharper, то он покажет, что здесь имеются лишние пространства имён: imgur.com/kYvWJqe В таком случае серые слова можно безопасно убрать.
    Ответ написан
    Комментировать
  • Не могу вызвать private C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Во-первых, приводите текст ошибки, чтобы мы могли вам помочь.
    А во-вторых, в методе test1() не хватает точки с запятой в третьей строчке.
    Ответ написан
    Комментировать
  • Как лучше создать экземпляр класса?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Первый способ создаёт поле класса, второй способ - локальную переменную. Если в других методах нужен уже ранее созданный экземпляр, или при повторном вызове этого метода нужен тот же экземпляр, то делайте поле. Если при каждом вызове метода нужен новый экземпляр, то делайте локальную переменную. Помните, что после окончания метода все данные локальных переменных удаляются (если вы не возвращаете экземпляр через возврат значения метода).
    Ответ написан
    5 комментариев
  • Как настроить принимаемые значения некоторых полей в сущности?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно использовать тип данных enum в записи. Enum для этого и предназначен. В базе будет использоваться число, а при получении данных из базы будет строка.
    Вот стандартное решение в доке доктрины.
    Если enum только один, то можно так и сделать. Но часто enum-ов много, и тогда я делаю вот такой способ:

    Базовый класс:
    <?php
    namespace AppBundle\DBAL;
    
    use Doctrine\DBAL\Types\Type;
    use Doctrine\DBAL\Platforms\AbstractPlatform;
    
    abstract class EnumType extends Type
    {
        protected $name;
    
        public static $VALUES = array();
    
        public static function getValues()
        {
            $values = array();
            foreach (static::$VALUES as $value) $values[$value] = $value;
            return $values;
        }
    
        public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
        {
            $values = array();
            foreach (static::$VALUES as $value) $values[] = "'$value'";
            return "ENUM(".implode(", ", $values).") COMMENT '(DC2Type:$this->name)'";
        }
    
        public function convertToPHPValue($value, AbstractPlatform $platform)
        {
            return $value;
        }
    
        public function convertToDatabaseValue($value, AbstractPlatform $platform)
        {
            if (!in_array($value, static::$VALUES)) {
                throw new \InvalidArgumentException("Invalid value '$value' for enum '$this->name'.");
            }
            return $value;
        }
    
        public function getName()
        {
            return $this->name;
        }
    }

    И теперь легко сделать нужный enum:
    <?php
    namespace AppBundle\DBAL;
    
    class MyType extends EnumType
    {
        protected $name = 'mytype';
    
        const FIRST = 'first';
        const SECOND  = 'second';
    
        public static $VALUES = array(self::FIRST, self::SECOND);
    }


    Ну и в настройке указать тип:
    doctrine:
        dbal:
            driver:   "%database_driver%"
            host:     "%database_host%"
            port:     "%database_port%"
            dbname:   "%database_name%"
            user:     "%database_user%"
            password: "%database_password%"
            charset:  UTF8
            types:
                mytype: AppBundle\DBAL\MyType
            mapping_types:
                enum: string


    Теперь можно указать его в сущности:
    class MyEntity
    {
        /**
         * @var integer $id
         *
         * @ORM\Column(type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;
        
       // ....
        
        /**
         * @var string
         *
         * @ORM\Column(type="mytype")
         */
        protected $type;


    Для чего делать отдельный класс для каждого enum-а? Для типизации значений. Например, можно было взять список значений: $types = MyType::getValues(), или проверить тип: if ($val->getType() == MyType::FIRST) {}
    Ответ написан
    Комментировать