• Symfony2: как в форме сделать выбор через модальное окно?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    используйте тип sonata_type_model_list
    https://sonata-project.org/bundles/doctrine-orm-ad...

    Просто поставьте тип поля sonata_type_model_list и всё:
    ->add('user', 'sonata_type_model_list')
    Ответ написан
  • Symfony2. Как указать правильный путь до бандла?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    а как вы передаёте файлы проекта на прод? Через git? Добавлен ли vendor в репозитории?

    Возможно, композер должен обновить autoload. Сделайте команду в консоли
    php composer.phar dump-autoload -o

    UPD
    composer не обязательно устанавливать. Это просто php-файл, только заархивированный - в формате phar. Для установки локально нужно выполнить вот такую команду в консоли:
    php -r "readfile('https://getcomposer.org/installer');" | php

    Эта команда скачает композер и положит его в файл composer.phar в текущую папку. (подробнее - см. документацию composer-а - https://getcomposer.org/doc/00-intro.md)
    В дальнейшем, для запуска используйте команду php composer.phar <команда>

    Лучше всего управлять вендорами с помощью composer-а и на dev, и на prod.
    На своей локальной машине вы используете команду composer update для установки новых пакетов и обновления уже существующих. При обновлении все установленные пакеты сохраняются в файл composer.lock, который хранит информацию о установленных версиях каждого пакета.
    В репозиторий вы добавляете файлы composer.json и composer.lock, а папку vendor добавлять в репозиторий не нужно. На prod-сервере для установки вендоров нужно запустить команду php composer.phar install, которая возьмёт нужные версии пакетов из composer.lock и установит их в папку vendor, а также обновит файл autoload-а.
    Ответ написан
    4 комментария
  • Как написать запрос в doctrine?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    > Items выводятся все равно все, даже isActive = 0
    нужно ставить не 1, а true:
    ->where('i.isActive = true')
    Я не подскажу, как сделать выборку только категорий, имеющих вложенные элементы, но вы легко сможете не выводить их в шаблоне:
    {% for category in categories if category.items is not null %}
    {{category.name}}
    {% for item in category.items %}
    - {{ item.name }}
    {% endfor %}
    {% endfor %}


    Если у вас есть сложные запросы, то рассмотрите возможность использования NativeQuery:
    doctrine-orm.readthedocs.org/en/latest/reference/n... (на русском odiszapc.ru/doctrine/native-sql)
    Ответ написан
  • Как добавить изображения в Sonata Admin?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Я использую VichUploaderBundle для сохранения картинок в сущность. Получается достаточно удобно (мне не нравится только то, что нельзя в twig нельзя напрямую выводить item.picture, а приходится выводить через vich_uploader_asset(item, 'image')).

    А в админке я вывожу миниатюру картинки через хелпы
    $helper = $this->getConfigurationPool()->getContainer()->get('vich_uploader.templating.helper.uploader_helper');
    $src = $helper->asset($this->getSubject(), 'image');
    $formMapper->setHelps([
        'picture' => "<img src=\"$src\" width=\"100\">",
    ]);


    Но это я использую в случае, если картинку нужно добавлять посетителю, а если я сам добавляю в админке, то я использую с помощью FMElfinderBundle.
    Ответ написан
    Комментировать
  • Какие есть сервисы с бесплатной музыкой и звуками?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    добавлю ещё из своей коллекции (не указанные ранее):
    sampleswap.org
    freemusicarchive.org
    Ответ написан
    Комментировать
  • Стоит ли использовать типы данных ENUM & SET в БД MySQL?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Set я не использовал, а вот Enum использую успешно. Используется он очень просто. Причём я создаю отдельный класс для каждого enum-а в проекте, что даёт некоторые преимущества, например, простой доступ к массиву допустимых значений.

    Я создаю в проекте папку DBAL, и там создаю такие классы:
    <?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()
        {
            return array_combine(static::$VALUES, static::$VALUES);
        }
    
        public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
        {
            $values = array_map(function($val) { return "'".$val."'"; }, static::$VALUES);
    
            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 GenderType extends EnumType
    {
        protected $name = 'gender';
        public static $VALUES = array('male', 'female');
    }

    Теперь нужно научить доктрину их использовать:
    doctrine:
        dbal:
            # ...
            types:
                gender: AppBundle\DBAL\GenderType
            mapping_types:
                enum: string
                gender: gender

    Теперь можно использовать в описаниях сущностей:
    <?php
    
    namespace AppBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Gedmo\Mapping\Annotation as Gedmo;
    
    /**
     * @ORM\Entity
     */
    class Person
    {
        // other fields
    
        /**
         * @var string
         *
         * @ORM\Column(type="gender")
         */
        protected $gender;
    
        // setters & getters
    }

    И в любом месте кода можно получить список доступных значений:
    foreach (GenderType::$VALUES as $gender) {
        echo $gender;
    }


    Насчёт типа SET - думаю, нужно написать подобный класс, умеющий переводить из значения php в формат sql, который корректно сохранит в поле. Но по мне, для языков лучше использовать simple_array, и не мучаться с ними.
    Ответ написан
    4 комментария
  • Каковы тонкости реализации пользовательских шаблонов Symfony2 + Twig?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Попробуйте дать пользователю возможность писать свой HTML, но ограничьте возможности с помощью HTMLPurifier-а (о нём на хабре, есть бандл). Он видится достаточно надёжным для того, чтобы пользователь не смог ввести ничего опасного. Дать ему возможность ввести основные html-теги и классы, а внешний вид - в отдельном css.
    Ответ написан
    Комментировать
  • Как создать symfony форму в случае standalone использования компонента форм?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Всё о формах написано в документации: symfony.com/doc/current/book/forms.html#book-form-...

    В контроллере создавайте сразу форму:
    $form = $this->createForm(new SignInForm(), null, array(
        'action' => $this->generateUrl('login_check'),
        'method' => 'POST',
    ));


    Насколько я понял, вашей форме не нужно передавать объект $user. Если всё-таки нужно, то укажите его вместо null.
    Параметры формы (action и method) лучше указывать тоже в классе формы. Тогда получится ещё проще:
    $form = $this->createForm(new SignInForm());
    Ответ написан
  • Хорошая архитектура symfony app?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Хорошая архитектура - это очень понятная архитектура, когда для каждого типа задачи созданы логичные и понятные средства. Когда приложение построено таким образом, то его получится и тестировать, и изменять/расширять. Если нужно добавить новый класс, то в хорошей архитектуре понятно, куда его нужно добавить.

    Всего существует не так много типов задач:
    1) хранение данных приложения - модель. Модель ничего не должна знать о базе данных.
    2) слой доступа к БД - репозиторий. Вся работа с БД - здесь, и всё, что связано с одной сущностью - в репозитории этой сущности. Если нужно взаимодействие нескольких сущностей, то, скорее всего, репозиторий одной из сущностей не подойдёт, эта задача пойдёт в сервис.
    3) бизнес-логика - сервисы. Сервисы умеют получать данные (модели), обращаясь к репозиториям или лучше к другим сервисам (служебным).
    4) служебные задачи - сервисы. Например, кэширование данных реализуется в специальном сервисе.
    5) отображение данных - шаблоны. Весь html - только в шаблоне, а также логика отображения тоже в нём (вывод списков, некоторые фильтры)
    6) подготовка данных к отображению - контроллер. Запрос пользователя приходит в контроллер, контроллер же обращается к сервису (или в простом случае к репозиторию) за данными. Возможно, для обработки запроса нужно получить данные из нескольких сервисов/репозиториев, а скорее, правильный сервис сам подготовит все данные для запроса.

    Получается, контроллер вызывает сервис, который предоставит готовые данные, и будет очень простым - "тонким". Основной код, отвечающий за работу задач приложения - в сервисах. Один и тот же сервис часто будет вызываться из разных контроллеров. Работа с БД - не в сервисе и уж точно не в контроллере, для этого нужен репозиторий.
    Если структура БД изменится, то в хорошей архитектуре поменять придётся только репозиторий, но не сервисы/контроллеры. С другой стороны, изменение схемы БД чаще всего связаны с новыми фичами, поэтому придётся добавить новый сервис или изменить существующий.
    Модель же умеет не так много - выводить свои данные, возможно, в разных форматах (данные, возвращаемые моделью не обязаны совпадать с полями сущности - см. пример в комментарии keltanas в ответе Владимир Балин).

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

    Получается, контроллеры "тонкие", модели "тонкие", а репозитории гораздо толще. Сервисы тоже "толстые", но их хорошо разбивать на отдельные сервисы, чтобы не было вся логика проекта в одном классе, и тогда каждый сервис уже не такой уж "толстый".

    P.S. Я вот написал, что модель не должна ничего не знать о базе данных, получается, что неправильно использовать аннотации для маппинга полей сущности на поля таблиц. После ответа на этот вопрос я, скорее всего, пересмотрю свои взгляды на @ОRМ-аннотации в моделях.
    Ответ написан
    23 комментария
  • Массивы деревья на php почему исчезает дерево?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вывод подобных массивов - задача для Twig. Это делается простейшим способом:
    {% extends '::layout.html.twig'%}
    
    {% block content %}
    <ul>
    {% for items in tree %}
    {% for item in items %}
        <li data-node="{{ item.id }}" style="position:relative;" class="list-group-item">
             <a href="/wikiedit/{{ item.id }}">{{ item.name }}</a>
             <i class="glyphicon glyphicon-remove pull-right" style="cursor:pointer;" onclick="R.wiki.delete({{ item.id }});"></i>
        </li>
    {% endfor %}
    {% endfor %}
    </ul>
    {% endblock %}

    Это если нет вложенных элементов. Если есть вложенные, то нужно сделать отдельный шаблон (без extends), и тут его вызывать через include.

    А у вас есть ещё ошибка в выводе - метод $str .= $this->viewtree($arr, $r['id']); вызывается сначала для массивов первой вложенности, а у них нет ключа id. Тут нужно сделать два разных метода - один будет обрабатывать первый уровень вложенности, а второй метод - сами элементы с данными.
    И если у вас нет вложенных друг в друга элементов с данными, то рекурсию использовать не стоит. У вас же явно видно, что элементы разных уровней вложенности имеют разный формат данных, значит, обрабатывать их одним методом некорректно.
    Ответ написан
    Комментировать
  • Как многократно использовать сущность?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    У вас две возможности:
    1) либо разрешить, чтобы поля могли быть пустыми - nullable
    2) либо установить значение по умолчанию для поля.
    namespace YourBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity
     */
    class User
    {
        /**
         * @ORM\Column(type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
        /**
         * @ORM\Column(type="string", length=100, nullable=true)
         */
        private $firstName;
    
        /**
         * @ORM\Column(type="string", length=100, nullable=true)
         */
        private $lastName;
    
        /**
         * @ORM\Column(type="string", length=100)
         */
        private $username = "";
    
        /**
         * @ORM\Column(type="string", length=100, unique=true)
         */
        private $email;
    
        /**
         * @ORM\Column(type="string", length=100)
         */
        private $password;
    
        // getters & setters
    }


    Смотрите, поля firstName и lastName отмечены как nullable=true, а поле username имеет значение по умолчанию (пустая строка).

    P.S. не забудьте добавить поле salt помимо пароля.
    P.P.S. или лучше возьмите FOSUserBundle, и не парьтесь, там уже есть отличный набор полей для юзера, плюс возможность добавить свои.
    Ответ написан
  • Расположение тегов в head html?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    можно так сделать. Нужно поместить в шаблон:
    <head>
        <script id="div-tpl" type="text/html">
            <div>data</div>
        </script>
    </head>

    так как script имеет тип не text/javascript, то выполняться как код он не будет. К тому же, разметка из этого <script> не будет добавлена в DOM. Но к содержимому можно получить доступ по его id ($('#div-tpl')).

    Вот посмотрите, как это использует John Resig (автор jQuery):
    ejohn.org/blog/javascript-micro-templating
    Ответ написан
    Комментировать
  • Кто может дать пример парсинга json в C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Json.NET
    Пакет NuGet

    В пустой WPF проект добавьте listBox1, установите через NuGet пакет, и замените MainWindow.xaml.cs на этот код:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    using Newtonsoft.Json;
    
    namespace WpfApplication9
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                Data data = JsonConvert.DeserializeObject<Data>(json);
                foreach (Item item in data.Response.Items)
                {
                    listBox1.Items.Add(new ListBoxItem() { Content = item });
                }
            }
        
            class Data
            {
                public Response Response;
            }
    
            class Response
            {
                public int Count;
                public Item[] Items;
            }
    
            class Item
            {
                public int Id;
                public int Owner_id;
                public string Artist;
                public string Title;
                public int Duration;
                public string Url;
                public int Genre_id;
    
                public override string ToString()
                {
                    return string.Format("{0} ({1})", Title, Url);
                }
            }
    
            string json = @"{
      ""response"":{
      ""count"":198,
      ""items"":[
      {
        ""id"":340125039,
        ""owner_id"":34254853,
        ""artist"":""IOWA"",
        ""title"":""Маршрутка"",
        ""duration"":190,
        ""url"":""http:\/\/cs7-5v4.vk-cdn.net\/p12\/630de313d73392.mp3?extra=fNremCtKgl4pnCbyZ0q-LOBIfvRoUf5QGo8oWwYrOCza6AlSUOdYIImUideFd3hpPa325ckSD_c3zQ5FNluMS80lrSJiM6FC"",
        ""genre_id"":9
      },
      {
        ""id"":340120602,
        ""owner_id"":34254853,
        ""artist"":""Stromae"",
        ""title"":""Tous Les Memes"",
        ""duration"":168,
        ""url"":""http:\/\/cs7-1v4.vk-cdn.net\/p18\/600b1a66ef64d6.mp3?extra=GNP79R4nHR39dY50YJ_ddDxEuX6SQcxtG3nCgzI0LdvxoaLh5lQ2qZqqpABCqPoXOISyNzWOqMZx4_nTL3bQ31i57z-UXl4V"",
        ""lyrics_id"":99395752
    
      }
      ]
      }
    }";
    }
    }
    Ответ написан
    Комментировать
  • Как организовать синхронизацию игрового мира между клиентом и сервером?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Посмотрите вопрос https://toster.ru/q/142555, в нём есть некоторые моменты, которые вам пригодятся. Я там привожу несколько ссылок "на почитать".

    А решение вашей задачи будет очень сильно зависеть от количества игроков в одной локации. Если их мало (скажем, меньше пары десятков), то каждая такая локация, скорее всего, будет независимой от других, и задача синхронизации станет сильно проще - меньше объектов будут требовать синхронизации. И на одном сервере поместится несколько таких локаций.

    Если игроков много (сотня и больше), то архитектура очень сильно усложнится. Настолько, что начинать учиться на таком проекте нет никакого смысла. Почитайте про это тот вопрос, что я привёл выше.

    Какая планируется технология на сервере и на клиенте? (язык программирования, платформа)
    Ответ написан
    3 комментария
  • Как организовать разработку с GIT, если есть множество задач, каждая на своей ветке, и все они должны быть доступны на тестовом сайте в любое время?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    По какой методике вы ведёте работу? Git flow или что-то наподобие?
    Сливайте всё оттестированное в master, или в preview, как посоветовал 183614956, или в какую-то ещё ветку.
    Ответ написан
  • Как сгруппировать значения в массиве?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Наверное, вам нужно учитывать событие на всём протяжении длительности события, то есть учитывать его не только в день начала, но и в каждый день, когда оно длится.
    Тогда нужно для каждого события сделать цикл, и для каждого дня в интервале считать это событие:
    $dates = array();
    foreach ($events as $event) {
        $dateStart = \DateTime::createFromFormat('Y-m-d', $event['date_start']);
        $dateEnd = \DateTime::createFromFormat('Y-m-d', $event['date_end']);
        while ($dateStart <= $dateEnd) {
            $date = $dateStart->format('Y-m-d');
            if (!isset($dates[$date])) $dates[$date] = 0;
            $dates[$date]++;
            $dateStart->modify('+1 day');
        }
    }
    Ответ написан
    Комментировать
  • Как переопределить значение свойства сущности при помощи сервиса в Symfony2?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    прямо так - никак не сделать. Нужно достаточно сильно изменять работу с сущностью.
    1) Можно сделать прокси-класс, имеющий расширенную логику в части некоторых свойств объекта, и передающий остальные свойства без изменений.
    2) Можно обрабатывать нужные свойства отдельным вызовом перед передачей объекта в шаблон - передать объект сущности в метод сервиса, и там изменить значения свойств.
    3) Можно поставить обработчик событий, который будет перехватывать системные события загрузки сущности и в обработчике изменять объект.
    Сущности доктрины - это просто обычный класс. И в идеале не стоит передавать в неё что-то внешнее, привносить лишнюю зависимость (например, не надо передавать текущего пользователя, имеющего роли).
    Ответ написан
  • Как в Symfony2 подружить select без multiple со свойством Entity, которое ManyToMany?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    для ограниченного пользователя используйте поле без multiple, а потом после отправки формы, но до сохранения превращайте его в ArrayCollection - прямо в то же поле создавайте коллекцию и добавляйте в неё вашу одну категорию.
    Ответ написан
    Комментировать
  • Как пользоваться фильтрами в Symfony2?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Сделайте QueryBuilder, и в нём сразу поставьте нужное условие. А в каждой странице добавляйте к билдеру дополнительные условия.
    Как вариант, создать родительский класс для всех контроллеров, и туда поставить метод, создающий этот QueryBuilder и возвращающий ссылку на него.
    Но гораздо лучше будет создать сервис, который инкапсулирует всю работу с сущностью, и сделать у сервиса несколько методов, возвращающих уже готовую сущность, возможно, из кэша.
    Ответ написан
  • Давайте соберем все актуальные ресурсы по фреймворку Laravel?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    https://github.com/chiraggude/awesome-laravel - большая пополняемая подборка материалов по Laravel.
    Есть ещё большая пополняемая подборка больших пополняемых подборок :)
    Ответ написан
    Комментировать