Миграция Doctrine с аннотаций на атрибуты

Начиная с PHP 8 в PHP были добавлены атрибуты, которые доступны теперь нативно. В будущем они полностью заменят аннотации. Библиотеки стали переходить на использование нативного синтаксиса.

Если вы используете doctrine, то при миграции на новую версию PHP возникает задача миграции аннотаций на атрибуты.

Сделать это можно при помощи rector. Для этого нужно:

  • Добавить rector как dev-зависимость: composer require rector/rector --dev
  • Создать конфигурационный файл в корне проекта с именем rector.php
<?php

declare(strict_types=1);

use Rector\Doctrine\Set\DoctrineSetList;
use Rector\Symfony\Set\SymfonySetList;
use Rector\Symfony\Set\SensiolabsSetList;
use Rector\Config\RectorConfig;

return function (RectorConfig $rectorConfig): void {
    $rectorConfig->sets([
        DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
        SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES,
        SensiolabsSetList::FRAMEWORK_EXTRA_61,
    ]);
};
  • Запустить вызвав в консоли: vendor/bin/rector process src где src — папка, для которой нужно запустить обработку. Можно также ограничить только директорией с сущностями — тогда src/Entity
  • Поправить конфигурацию doctrine в файле config/doctrine.yaml в маппинге нужно удалить строку: type: annotation

MySQL — Медленная операция COUNT(*) / COUNT(id)

Жил я себе спокойно, разрабатывал приложения, углубился в базы данных, почитал про highload, и умные люди на конференции в прошлом году заявляли, что в PgSQL операция count(id) весьма медленная. Интересно, никогда об этом не думал, что там — посчитать по индексу… а, нет, не по индексу, полный скан, значит не так уж и быстро…

В большинстве случаев, не требуется знать точное значение count() для таблицы, когда нет условий на выборку оптимальнее использовать вместо классического

SELECT count(id) FROM <table>

запрос на статистику таблицы:

show table status like '<table name>';

Я тестировал на таблицы в ~20 тыс строк, скорость работы отличается значительно

  • 0,63 ms для обычного точного count, при этом реальное число 21902
  • 0,47 ms для статуса таблицы, а тут число 22008

То есть, чем таблица больше тем актуальнее использовать приближенный count вместо живого-настоящего.

 

Как задавать права доступа в Symfony используя базу данных (Voters used database)

С тех пор, как вся конфигурация Symfony кешируется с контейнером для лучшей производительности , мы по очевидным причинам не должны использовать базу данных что бы где-то «напечатать» новую конфигурацию, нам нужно что-то умнее.

Использование Symfony Voters

Symfony использует Voters что бы определить доступ к URL и другим ресурсам. Множество voters которые входят в комплект поставки вместе с Symfony security  которые используют конфигурацию в security.access_control что бы определить нужно ли давать права на запрос или нет.

Continue reading

Деревья в PHP + Twig 2.0

Предположим, есть задачка, вывести дерево.

Под рукой PHP, а как view — Twig.

Пусть структура данных на backend будет такой:

$tree = [

            [
                'name' => 'foo',
                'children' => [
                    [
                        'name' => 'bar',
                        'children' => [
                            [
                                'name' => 'baz',
                                'children' => [

                                ],
                            ],

                            [
                                'name' => 'baz',
                                'children' => [

                                ],
                            ],


                        ]
                    ]
                ]
            ]

        ];

Тогда простроить дерево можно так:

{% macro makeTree(node) %}
    {% import _self as self %}
    
  • {{ node.name }} {% if node.children|length %}
      {% for child in node.children %} {{ self.makeTree(child) }} {% endfor %}
    {% endif %}
  • {% endmacro %} {% from _self import makeTree %} {% if tree %}
      {% for node in tree %} {{ makeTree(node) }} {% endfor %}
    {% endif %}

    Получится вложенный список из ul и li.

    Как выбрать только необходимые поля в реляции (связи) Laravel

    Иногда требуется выбрать не всю таблицу, а только некоторые поля из связи в контроллере. Сделать это можно так:

    $user = User::with(['department' => function ($q) {
                    $q->select(['id','name']);
                } ])->get();
    

     

    Blade Foreach: первый/последний элемент массива

    Шаблоны blade используются для представления данных, нам нужно добавить некоторые проверки и If структуры. Один из примеров это цикл @foreach, когда нужно выполнить какие-либо действия с первым или последним элементом массива.  Blade не поддерживает эту функциональность самостоятельно, итак, что же нам делать?

    Под капотом, Blade-файлы в действительности преобразуются в чистый PHP.  Итак, хитрость в том, что мы можем использовать любую функцию из PHP. И с массивами у нас есть две полезных функции что бы получить первый и последний элементы массива:

    • end($array) – перемещает внутренний указатель массива к последнему элементу и возвращает его значение.
    • reset($array) – перемещает внутренний указатель массива к первому элементу и возвращает его значение.

    Итак, как это выглядит в настоящем примере с Blade? Предположим, мы хотим скрыть каждый элемент div, кроме первого:

    @foreach ($menu as $item)
      <div @if ($item != reset($menu)) class=«hidden» @endif>
        <h2>{{ $item->title }}</h2>
      </div>
    @endforeach

    Похожий пример — что если мы хотим добавить CSS-класс к последнему элементу меню?

    @foreach ($menu as $item)
      <div @if ($item == end($menu)) class=«no_margin» @endif>
        <h2>{{ $item->title }}</h2>
      </div>
    @endforeach

    Это все. Как вы можете видеть Blade не только шаблонизатор, он обладает мощью структур и функций PHP

     

    Перевод

    Человекочитаемый размер файла в PHP

    Маленькая функция для преобразования размера  в байтах в человекочитаемый вид:

    <?php
    function human_filesize($bytes, $decimals = 2) {
        $size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
        $factor = (int) floor((strlen($bytes) - 1) / 3);
        return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor];
    }
    

    Использование

    <?php
    echo human_filesize(filesize('example.zip'));

    Источник

    Миграция пользователей на безопасный алгоритм хеширования в Symfony

    Ваше приложение может использовать старый, небезопасный алгоритм хеширования для хранения пароля, такой как MD5 (без использования соли)

    Эта статья объясняет как преобразовать уже имеющиеся пароли, зашифрованные уязвимым алгоритмом в пароли зашифрованные с использованием безопасного метода хеширования (например с использованием Bcrypt )

    Что бы решить проблему, мы сделаем конвертацию на лету, когда пользователь успешно входит в систему. Будем использовать интерфейс EncoderAwareInterface

    login listener и использовать не очень хорошо известные параметры в security.yml.
    Continue reading

    Laravel: Like для коллекций

    В коллекциях Laravel 5.1 нет функции like для коллекций, но можно написать подобное поведение при помощи filter.

     $searchBy = 'gr';
     $data = collect(array('name' => 'gregg', 'name' => 'john', 'name'=> 'alice'));
            $data = $data->filter(function ($item) use ($searchBy) {
                return (bool) preg_match('#^'.preg_quote($searchBy).'.*$#us', $item['name']);
            });
    
    SQL like preg_match
    % .*
    _ .