Задать вопрос
  • Как можно решить проблему с доступам к методам PHP классов унаследованным от одного класса?

    Красиво сделать не получится. Но можно сделать так, чтобы хотя бы на тестах оно упало и показало, что нельзя так делать.

    <?php
    
    class Node
    {
        protected function func1() {
            print "NODE PARENT; ";
        }
    }
    class NodeA extends Node
    {
        public function FUNC2() {
            print "NODE A; ";
        }
    
        // Используем метод родителя внутри этого класса
        public function func1Overrided() {
            print "From parent: " . parent::func1();
        }
    
        // Переопределяем метод так, чтобы его нельзя было использовать
        protected function func1() {
            throw new \Exception("Нельзя вызывать этот метод из NodeB");
        }
    }
    
    class NodeB extends Node
    {
        // Функция инициализации
        public function onInit(NodeA $a): void
        {
            // Сделал так, чтобы не мокать api )
            (function (?NodeA $a) {
                $a->FUNC2(); // Метод успешно вызывется так как он public
                $a->func1();  // Метод теперь кидает исключение, использовать не получится
            })($a);
        }
    }
    
    $nodeA = new NodeA;
    $nodeA->func1Overrided(); // Работает вызов метода funс1 из родителя
    
    $nodeB = new NodeB;
    $nodeB->onInit($nodeA); // Выдаёт ошибку, нельзя использовать метод func1 из класса NodeB


    Но у меня для вас совет: старайтесь как можно меньше использовать наследование. Из моего опыта могу сказать, что всякий раз, когда я с помощью наследования пытаюсь решить хоть сколько-нибудь сложную задачу, постоянно вылазят такие подводные камни, что жить не хочется. Используйте композицию и интерфейсы, и вам не придётся заниматься подобной ерундой.

    Вот минимально-инвазивное решение, которое позволит и наследование сохранить (если оно прям ну вот сильно надо), и решить проблему при помощи композиции, основанной на трейте.

    <?php
    // Делаем общий трейт для всех классов
    trait Func1 {
        private function func1() {
            print "FUNC1; ";
        }
    }
    
    class Node
    {
        // Включаем трейт
        use Func1;
    }
    
    class NodeA extends Node
    {
        // Включаем трейт
        use Func1;
    
        public function FUNC2() {
            print "FUNC 2 NODEA; ";
        }
    }
    
    class NodeB extends Node
    {
        // Включаем трейт
        use Func1;
    
        // Функция инициализации
        public function onInit(NodeA $a): void
        {
            // Добавить зависимость
            (function (?NodeA $a) {
                $a->FUNC2(); // Метод успешно вызывется так как он public
                $a->func1();  // Метод использовать не получится, т.к. он private
            })($a);
        }
    }
    
    $nodeA = new NodeA;
    
    $nodeB = new NodeB;
    $nodeB->onInit($nodeA); // Выдаёт ошибку, нельзя использовать метод func1 из класса NodeB
    Ответ написан
  • Насколько актуальна книга Марка Саммерфильда?

    Не читал, но не осуждаю)

    Я считаю, что если многие советуют, то можно брать, если хотите.

    Go - язык немного особенный в плане устаревания информации. Если, например, купить книгу по JavaScript того года, то это будет супер-вредно, потому что в те времена это был чуть ли не другой язык.

    А создатели Go с самого начала заявили, что обратная совместимость будет одной из основных характеристик языка. Роб Пайк вообще говорил, что Go 2 никогда не будет.

    Так же, из-за простоты языка существует великолепный феномен, когда разные программисты, решающие одну и ту же задачу, напишут практически одинаковый код.

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

    P. S. Совет лично от меня: читайте книги по программированию на английском. Очень часто переводчики просто бредят как ChatGPT, и некоторые книги просто невозможно читать.
    Ответ написан
    2 комментария
  • GitHub, GitLab или BitBucket?

    Я рекомендую Gitlab
    - Можно хостить весь Gitlab у себя. Вначале это может показаться лишним, но многие работодатели так делают, поэтому навыки по работе с Gitlab пригодятся.
    - Отличный CI. Как по мне, гораздо лучше чем Github actions
    - Проекты в Gitlab можно спокойно и очень просто синхронизировать с тем же самым Github прямо из интерфейса Gitlab, таким образом мы получаем преимущества обеих систем.

    bitbucket всё, забудьте о нём.
    Ответ написан
    7 комментариев
  • Как работать с slug в Symfony?

    В services.yaml вставил

    App\Entity\Page:
    autowire: true

    1. Уберите это.

    2. Используйте атрибут MapEntity, чтобы явно указать, на какое поле мапить параметр 'slug'

    <?php
    
    namespace App\Controller;
    
    use App\Entity\Page;
    use App\Repository\PageRepository;
    use Symfony\Bridge\Doctrine\Attribute\MapEntity;
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Attribute\Route;
    
    class PageController extends AbstractController
    {
        // ...
    
        #[Route('/{slug}', name: 'show-page')]
        public function show(
            #[MapEntity(mapping: ['slug' => 'slug'])] Page $page
        ): Response
        {
            return $this->render('/page/show.html.twig', [
                'slug' => $page->getSlug(),
                'page' => $page,
            ]);
        }
    }
    Ответ написан
  • Возможно ли передать props только после выполнения функции?

    Вы можете в любой момент менять props у компонента. Но просто надо следить за изменением при помощи watch внутри дочернего компонента.

    Composition API:
    watch(() => props.selected, (selection, prevSelection) => { 
       /* Реагируем на изменения... */ 
    })
    Ответ написан
    Комментировать
  • Можно ли уникализировать отправку форм с сайта без cms?

    Ответ выше про скрытое поле абсолютно верен.
    Если же вы по какой-то причине не хотите использовать скрытые поля, то можете просто назначить имя (или value) самой кнопке отправки формы.

    <input type="submit" value="save" name="loginForm">


    if (isset($_POST['loginForm'])){
        // Код
    }
    Ответ написан
    Комментировать
  • Как в goшном SOA сервисе работать с базой данных?

    Не знаю, какая у вас архитектура, но когда какая-то библиотека обрастает огромным количеством методов, то Go - это один из редких языков программирования, который, благодаря своей утиной типизации позволяет использовать шаблон проектирования "consumer interfaces", т.е. объявлять интерфейсы в месте, где они используются и включать в эти интерфейсы только те методы из огромной структуры базы данных, которые именно тут, на месте, и используются. Таким образом ваши интерфейсы будут минимальными, понятными, и искать их не придётся. Ну, и в добавок, старый добрый Dependency Injection решит массу наших проблем

    База данных:
    package db
    
    import "fmt"
    
    // Стуктура базы данных. Здесь нам даже и не нужно объявлять никаких интерфейсов
    type Service struct {
    	// ...
    }
    
    func NewDbService() (*Service, error) {
    	return &Service{}, nil
    }
    
    func (d *Service) CreateUser(username string, email string) {
    	fmt.Println(fmt.Sprintf("user %s with email %s is created", username, email))
    }
    
    func (d *Service) CreateProduct(name string) {
    	fmt.Println(fmt.Sprintf("product  %s is created", name))
    }


    Пользователь:
    package user
    
    // Тот самый интерфейс, который нам позволяет выбрать из структуры базы данных
    // только нужные нам здесь методы
    type dbUser interface {
    	CreateUser(username string, email string)
    }
    
    type Service struct {
    	db dbUser
    	// ...
    }
    
    func NewService(db dbUser) (*Service, error) {
    	return &Service{db}, nil
    }
    
    func (c *Service) New(username string, email string) {
    	c.db.CreateUser(username, email)
    }


    Товар:
    package product
    
    // Тот самый интерфейс, который нам позволяет выбрать из структуры базы данных
    // только нужные нам здесь методы
    type dbProduct interface {
    	CreateProduct(name string)
    }
    
    type Service struct {
    	db dbProduct
    	// ...
    }
    
    func NewService(db dbProduct) (*Service, error) {
    	return &Service{db}, nil
    }
    
    func (p *Service) New(name string) {
    	p.db.CreateProduct(name)
    }


    main.go
    package main
    
    import (
    	"test2/db"
    	"test2/product"
    	"test2/user"
    )
    
    func main() {
            // Инициализируем базу данных
    	dbService, _ := db.NewDbService()
            // Структура базы данных реализует интерфейс dbProduct, инжектим её
    	productService, _ := product.NewService(dbService)
            // Структура базы данных реализует интерфейс dbUser, инжектим её
    	userService, _ := user.NewService(dbService)
    
            // Пользуемся на здоровье...
    	userService.New("user1", "uswr1@example.com")
    	productService.New("product1")
    }
    Ответ написан
    Комментировать
  • Из-за чего может быть ошибка к подключению к серверу tcp://localhost:5432?

    docker compose при выполнении команды up создаёт между сервисами новую сеть по умолчанию.
    Каждому сервису в этой сети он назначает имя хоста, соответствующее имени самого сервиса и в сети по умолчанию, и в вашей кастомной сети app-dev. Вместо localhost ставим db.

    "ConnectionStrings": {
        "DefaultConnection": "User ID=postgres;Password=postgres;Host=db;Port=5432;Database=example;"
      }


    А вот снаружи сети докера, т.е. с вашей локальной машины из какого-то другого приложения вы сможете получить доступ уже через localhost (если у вас не Mac)
    Ответ написан
    Комментировать
  • Как добавить элемент в массив по условию JS?

    Элегантный способ - это не всегда хорошо. Если массив не занимает мегабайты данных, то самым простым способом будет создание нового выходного массива нулевой длины, а потом, просто перебирая элементы оригинального массива, вставляете их в новый массив, а в нужные моменты просто вставляйте сперва эту вставку , а потом сразу элемент оригинального массива.
    И не нужны вам эти "шикарные" reduce в данной ситуации. Обычный цикл for. Потому что reduce совершенно бесполезен конкретно в данном случае, а читаемость ухудшит.

    const arr = [
        {src: 'some-path-1', video: 'some-path-1'},
        {src: 'some-path-2', video: 'some-path-2'},
        {src: 'some-path', video: null},
    ];
    
    const result = [];
    for (let i = 0; i < arr.length; i++) {
      if (arr[i]?.video) {
        result.push({ src_video: arr[i].video });
      }
      result.push(arr[i]);
    }
    
    console.log(result);
    Ответ написан
    9 комментариев
  • Symfony каталог товаров с динамичной фильтрацией?

    Самое простое решение в данном случае - использовать в базе данных дополнительное поле с типом данных JSON, в котором и хранить объект с такими дополнительными характеристиками. Т.е. мы тут как бы совмещаем функционал нашей SQL базы данных с функционалом документных NoSQL баз.
    И доктрина и сами современные SQL базы позволяют искать данные по JSON.

    Более традиционный вариант - это создание двух таблиц. Первая таблица options_list будет содержать список самих характеристик. Поля могут быть, например:

    id - код характеристики,
    name - наименование характеристики

    Во второй таблице options_values будут храниться уже сами значения характеристик.
    И у неё будут поля:

    id - код значения характеристики,
    product_id - код товара из таблицы products (или как там у вас), для которого создается характеристика. FOREIGN KEY,
    option_id - код самой характеристики из таблицы options_list. FOREIGN KEY,
    value - значение характеристики 
    Ответ написан
  • Как скачать весь контент с сайта?

    Сайт полностью сгенерирован Javascript уже в браузере после загрузки страницы. В самом исходном коде html никаких данных нет. Поэтому скачать обычным образом его не получится.

    Хорошо, что прекрасные люди придумали для этой задачи расширения для браузера. Например, вот такое https://chromewebstore.google.com/detail/save-page...

    Нажав на кнопку расширения, будет скачана вся страничка полностью вместе со всеми текстами, стилями и изображениями в формате base64
    Ответ написан
  • Как решить зависимость класса от другого класса?

    У вас циклическая зависимость. Это первейший признак того, что вы что-то неправильно спроектировали. Выносите функционал, используемый обоими классами, в третий класс. Тогда вы избавитесь от циклической зависимости.
    Ответ написан
    Комментировать
  • MacBook Air M1 8/256 подойдёт для разработки?

    У меня Macbook Pro M1 Pro 16/512.
    Когда запускаешь какую-то IDE от Jetbrains типа Goland, то батарея тает на глазах, да и памяти она кушает хорошо.

    Если будете работать в VScode, то такой проблемы возникнуть не должно, если не увлекаться плагинами. Сам Go потреблять много ресурсов не будет. Основной потребитель памяти - это всегда большое количество вкладок в браузере.

    Но... Как только вам понадобится Docker, могут начаться проблемы. На Маке он не нативный, и может сжирать много памяти. У меня много чего в Докере запускается, и вкладок в браузере всегда несколько требуется, поэтому мне 16 прям впритык.

    Мой совет - брать машину с 16гб минимум, но тогда уже нет смысла брать Air...

    P. S. Мак в разы лучше работает с памятью, чем винда, поэтому сравнивать не всегда корректно.
    Ответ написан
    2 комментария
  • Как реогранизовать процесс разработки в IT-продукте?

    1. Git. Без него сейчас ну просто никак.
    2. Gitlab CI или GitHub actions для деплоя.
    3. Пишете скрипт, который при запуске на локалке создаёт новую базу и заполняет её фейковыми данными для тестов. Так новые разработчики не будут иметь доступ к данным.
    3. Новые разработчики будут иметь доступ к коду, смогут создавать свои ветки в Git, пушить эти свои ветки в ваш удаленный репозиторий Git и даже создавать merge request на слияние их ветки с основной веткой разработки. А вы уже сможете сделать ревью их кода и подтвердить слияние, либо отказать. Все права отлично настраиваются как в Gitlab, так и в GitHub.
    4. Если же вы вообще не хотите показывать даже код сторонним разработчикам, то тут без модулей либо даже микросервисов не обойдешься. Я бы начал пробовать с модулей.

    P. S. Если в голову придёт мысль переписать проект полностью, то десять тысяч раз подумайте перед этим. В моей практике нет ни одного успешного переписывания сложного проекта... Всегда нужны новые срочные фичи, и придется параллельно внедрять эти фичи в старую кодовую базу, одновременно с этим пытаться их внедрить в сырую переписываемую кодовую базу. Это может закончиться ещё более запутанным проектом, чем был оригинал.
    Ответ написан
    Комментировать
  • Как заблокировать работу с окном браузера при перезагрузке страницы?

    Всем "опасным" кнопкам по умолчанию устанавливаем атрибут disabled. Тогда их нельзя будет нажать.

    <button class="dangerous"  disabled>Сохранить</button>


    И сделать обработчик события загрузки страницы, который уберет этот самый атрибут с опасных кнопок по окончании полной загрузки страницы

    document.addEventListener(‘load’, function() { 
    	const dangers = document.querySelectorAll(".dangerous");
            dangers.forEach((item) => {
                  item.removeAttribute('disabled'):
            });
    }


    Если же вас беспокоят именно элементы, остающиеся на экране при старте перезагрузки, то самым простым решением было бы вообще удалить весь html страницы до перезагрузки, а потом спокойно её запустить. Либо вы можете точно так же в момент, когда вам надо запустить перезагрузку, установить всем опасным кнопкам атрибут disabled, а потом стартовать перезагрузку страницы.

    Но!!! Данный подход только лишь сделает жизнь пользователей более приятной. Не забывайте, что фронтенд-фрогнтендом, но бэкенд должен всё же уметь работать с такими запросами, которые не будут содержать в себе все нужные данные.
    Первое правило бэкенда - "Никогда не верь фронтенду и его данным, проверяй и валидируй всё"
    Ответ написан
    Комментировать
  • Как организовать структуру проекта с несколькими модулями Golang?

    Делаем три разных файла main.go под каждый сервис. Кладём их каждый в свою подпапку в директории, например "cmd".
    А для общих библиотек используем отдельную директорию, например "pkg", и будем импортировать отсюда функционал во все три сервиса.

    Т.е. у нас получается структура файлов:
    cmd/
          repeater/
                main.go
          controller/
                main.go
          executor/
                main.go
    pkg/
          c-library/
                clibrary.go
    go.mod


    И потом запускаем компиляцию:

    go build ./cmd/repeater
    go build ./cmd/controller
    go build ./cmd/executor


    Пример main.go
    package main
    
    import c_library "test/pkg/c-library"
    
    func main() {
    	c_library.HelloWorld()
    }


    И общая библиотека:
    package c_library
    
    import "fmt"
    
    func HelloWorld() {
    	fmt.Println("HELLO WORLD")
    }


    Весь проект у меня лежит в папочке test, и в файле go.mod надо бы указать следующее

    module test
    Ответ написан
    5 комментариев
  • Какую CMS выбрать для первого учебного проекта?

    Всегда всем советую Wordpress, потому что это уже не просто CMS, это целый мир.
    Однако, если по какой-то причине столкнётесь с непреодолимыми сложностями, то могу посоветовать Drupal. Это более сложная, но и более подходящая для будущих программистов CMS, потому что она написана на фреймворке Symfony, являющихся одним из столпов программирования на PHP.
    Т.е.:
    - Если надо обучить людей быстро создавать сайты и начать зарабатывать денюжку, то Wordpress без вариантов.
    - Если надо в итоге сделать из людей программистов, то можно рассмотреть Drupal
    - Битрикс забываем, как страшный сон.
    Ответ написан
    1 комментарий
  • Как правильно разрабатывать symfony приложение локально?

    Я использую для локальной разработки docker compose.
    В его конфигурационном файле собираю все нужные контейнеры (PHP, MySQL, Rabbit, Mongo и т.д.) в сеть. Для PHP делаю два контейнера из разных образов:
    Один из образа php-fpm, это сам сервер.
    Второй из образа php-cli, он используется как раз для всех консольных команд. Именню в него устанавливаю Composer.
    Для того, чтобы не писать для запуска консольных команд постоянно это заклинание "docker compose run ...", я делаю Makefile, в котором прописываю часто-используемые команды.
    И, набрав "make cc" на самом деле запускаю "docker-compose run --rm php-cli bin/console c:c"
    Чтобы это всё работало без необходимости постоянно перезапускать docker-compose, я файлы проекта пробрасываю внутрь контейнеров не копированием, а через вольюм, и когда я буду их менять, они будут меняться и внутри контейнера. Так же подключаю прямо внутрь контейнера .env файл, чтобы все переменные окружения туда пробросились.
    Я вам прямо закину кучу файлов из одного из своих старых проектов, может накопаете там идей каких-то для себя:
    1. docker-compose.yml
    version: '3.8'
    services:
      # Поднимаю сразу реверс-прокси, чтобы пробросить всё наружу на нужный мне порт.m
      nginx:
        build:
          context: ./.docker/nginx
          dockerfile: Dockerfile.dev
        volumes:
          - ./:/app
        depends_on:
          - php-fpm
        ports:
          - "8080:80"
    
      php-fpm:
        build:
          context: ./.docker/php
          dockerfile: Dockerfile.fpm-dev
          args:
            IMAGE_ENV: dev
        volumes:
          # Вот тут вся магия) Мы просто подключаем файлы проекта в виде вольюма
          # Т.е. имеем доступ ко всем файлам и изнутри и снаружи контейнера. 
          # Внутри контейнера файлы будут видны по пути /app
          - ./:/app
        environment:
          # Даже XDEBUG можно пробросить для IDE
          XDEBUG_CONFIG: 'discover_client_host=true client_host=127.0.0.1 log_level=0 client_port=9003'
          PHP_IDE_CONFIG: "serverName=Docker"
        env_file:
          - .env.local
        depends_on:
          - mysql
    
      # Делаю отдельный контейнер для комманд-лайн утилит, 
      # чтобы не проще запускать консоль Symfony. Мне так удобнее.
      # Здесь будет всё практически как и у php-fpm, только без самого php-fpm процесса
      php-cli:
        build:
          context: ./.docker/php
          dockerfile: Dockerfile.cli-dev
          args:
            IMAGE_ENV: dev
        volumes:
          - ./:/app
          - composer:/root/.composer/cache
        environment:
          XDEBUG_CONFIG: 'discover_client_host=true client_host=127.0.0.1 log_level=0 client_port=9003'
          PHP_IDE_CONFIG: "serverName=Docker"
        env_file:
          - .env.local
        depends_on:
          - mysql
    
      mysql:
        build:
          context: ./.docker/db
          dockerfile: Dockerfile
        volumes:
          - mysql:/var/lib/mysql
        env_file:
          - .env.local
        environment:
          MYSQL_ROOT_HOST: '%'  # needs to be enclosed with quotes
        ports:
          - "6603:3306"
    
    # Тут ещё два дополнительных вольюма. Один для кэша composer, а второй для файлов базы данных, чтобы она не убивалась при перезапуске наших контейнеров. Убивать мы её будем только сознательно. если нам надо её очистить.
    volumes:
      composer:
      mysql:


    2. Все три докерфайла
    Файлы с конфигурациями, которые будут копироваться внутри докерфайлов, кидать не буду, у вас всё своё будет в любом случае.

    Dockerfile.dev - для nginx.
    # Образ Nginx
    FROM nginx:alpine
    
    # Копируем файл с настройками
    COPY ./task/default.conf /etc/nginx/conf.d/default.conf
    
    # Устанавливаем рабочую директорию
    WORKDIR /app


    Dockerfile.fpm-dev - для сервера php-fpm
    # Образ php-fpm
    FROM php:8.2-fpm
    
    ARG IMAGE_ENV
    
    # Устанавливаем нужные пакеты и расширения
    RUN apt-get update && apt-get install -y -qq libpq-dev unzip libxml2-dev libicu-dev libonig-dev zlib1g-dev libpng-dev librabbitmq-dev \
        && docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd && docker-php-ext-install pdo_mysql && \
        docker-php-ext-configure intl && docker-php-ext-install intl && \
        pecl install xdebug amqp && rm -rf /tmp/pear && docker-php-ext-enable xdebug amqp && \
        rm -rf /var/lib/apt/lists/*
    
    # Use the default development configuration
    RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
    
    # Копируем файл с настройками xdebug
    COPY ./php.d/${IMAGE_ENV}/default.ini /usr/local/etc/php/conf.d/default.ini
    COPY ./php.d/${IMAGE_ENV}/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
    
    
    # Создаем пользователя app, чтобы не работать под рутом
    RUN adduser --disabled-password --gecos '' --uid 1000 app
    USER app
    
    # Устанавливаем рабочую директорию
    WORKDIR /app


    Dockerfile.cli-dev - это наш консольный php для запуска консольных команд. Тут практически то же самое, плюс тот самый Composer, ради которого это всё и писалось)
    # Образ php-cli
    FROM php:8.2-cli
    
    ARG IMAGE_ENV
    
    # Устанавливаем нужные пакеты и расширения
    RUN apt-get update && apt-get install -y -qq git unzip libpq-dev libxml2-dev libicu-dev librabbitmq-dev && \
        docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd && docker-php-ext-install pdo_mysql && \
        docker-php-ext-configure intl && docker-php-ext-install intl && \
        pecl install xdebug amqp && rm -rf /tmp/pear && docker-php-ext-enable xdebug amqp && \
        rm -rf /var/lib/apt/lists/*
    
    # Use the default development configuration
    RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
    
    # Копируем файл с настройками xdebug
    COPY ./php.d/${IMAGE_ENV}/default.ini /usr/local/etc/php/conf.d/default.ini
    COPY ./php.d/${IMAGE_ENV}/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
    
    # Качаем и устанавливаем composer
    RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/bin --filename=composer --quiet
    
    # Если всё же будете работать в контейнере под рутом, то заставляем composer работать под рутом
    # ENV COMPOSER_ALLOW_SUPERUSER 1
    
    # Создаем пользователя app, чтобы не работать под рутом
    RUN adduser --disabled-password --gecos '' --uid 1000 app
    USER app
    
    # Устанавливаем рабочую директорию
    WORKDIR /app


    3. И далее звезда шоу - Makefile с кучей разных скриптов, облегчающих жизнь.
    make init - чистая сборка проекта, тут можно очищать базу и загружать в нее фикстуры
    make up - просто запуск проекта, без очистки, перед началом обычного рабочего дня.
    make down - остановка проекта
    make cc - очистка кэша Symfony.
    И т.д. и т.п.
    # Если надо подключить shell переменные, то создаём тут же ..env.local файл и делаем.
    include .env.local
    export
    
    # Поддержка возможности запускать шелл-скрипты
    SHELL:=/bin/bash
    
    # Кэш
    c: cache-clear cache-warmup
    cc: cache-clear
    cw: cache-warmup
    cache-clear:
    	docker-compose run --rm php-cli bin/console c:c
    cache-warmup:
    	docker-compose run --rm php-cli bin/console c:w
    
    up: docker-up
    down: docker-down
    restart: docker-down docker-up
    init: docker-down init-common
    reset: docker-down-clear init-common
    init-common: clear docker-pull docker-build docker-up app-init
    test: test
    test-coverage: test-coverage
    test-unit: test-unit
    test-unit-coverage: test-unit-coverage
    
    # Если в контейнере другой пользователь, то будут ошибки доступа при редактировании файлов.
    # Поэтому надо сделать chown и chmod для папки var (кэш и логи)
    chown:
    	sudo chown -R 1000:1000 . # Тут указываем свои данные пользователя с хоста
    	sudo chmod -R 777 ./var/
    
    docker-up:
    	docker-compose up -d
    
    docker-down:
    	docker-compose down --remove-orphans
    
    docker-down-clear:
    	docker-compose down -v --remove-orphans
    
    docker-pull:
    	docker-compose pull
    
    docker-build:
    	docker-compose build
    
    app-init: composer-install app-wait-db app-migrations ready
    
    clear:
    	docker run --rm -v ${PWD}:/app --workdir=/app alpine rm -f .ready
    
    ready:
    	docker run --rm -v ${PWD}:/app --workdir=/app alpine touch .ready
    
    test:
    	docker-compose run --rm php-cli php bin/phpunit
    
    test-coverage:
    	docker-compose run --rm php-cli php bin/phpunit --coverage-clover var/clover.xml --coverage-html var/coverage
    
    test-unit:
    	docker-compose run --rm php-cli php bin/phpunit --testsuite=unit
    
    test-unit-coverage:
    	docker-compose run --rm php-cli php bin/phpunit --testsuite=unit --coverage-clover var/clover.xml --coverage-html var/coverage
    
    composer-install:
    	docker-compose run --rm php-cli composer -V
    	docker-compose run --rm php-cli composer install
    
    app-create-db:
    	docker-compose run --rm php-cli ./bin/console doctrine:database:create
    
    app-wait-db:
    	#docker-compose exec lkui-mysql mysqladmin --user=root --password=password ping --silent --wait=60 &> /dev/null
    	#sleep 20
    	until docker-compose exec -T mysql mysqladmin ping -u root -P ${MYSQL_TCP_PORT} -p${MYSQL_ROOT_PASSWORD} | grep "mysqld is alive" ; do >&2 echo "MySQL is unavailable - waiting for it... " ; sleep 5 ; done
    
    app-migrations:
    	docker-compose run --rm php-cli ./bin/console doctrine:migrations:migrate --no-interaction
    
    app-fixtures:
    	docker-compose run --rm php-cli ./bin/console doctrine:fixtures:load --no-interaction
    
    migration:
    	docker-compose run --rm php-cli ./bin/console make:migration
    
    migrate:
    	docker-compose run --rm php-cli ./bin/console doctrine:migrations:migrate #--no-interaction
    
    fixtures:
    	docker-compose run --rm php-cli ./bin/console doctrine:fixtures:load
    Ответ написан
    2 комментария
  • Как лучше описывать документацию API в PHP 8.1 через аннотации или атрибуты?

    Используйте атрибуты.
    Да, на данный конкретный момент документации по аннотациям больше, но, во-первых, как верно заметил уважаемый BoShurik , атрибуты почти всегда полностью соответствуют аннотациям в плане параметров, а, во-вторых, сообщество PHP целенаправленно идёт в сторону использования атрибутов, и однажды, когда-нибудь, при апгрейде вам прямо придётся переписать аннотации на атрибуты, потому что какая-то новая версия откажется от аннотаций.
    Ответ написан
    1 комментарий