Задать вопрос
  • Как найти иконки OK и VK в CSS content: "\f14b";?

    Вижу, что у вас это при помощи шрифта сделано.
    Можете используемый шрифт скачать и посмотреть, что там внутри. fa - это скорей всего font awesome.
    f263: https://fontawesome.com/v4/icon/odnoklassniki
    f189: https://fontawesome.com/v4/icon/vk

    Вы можете также:
    - Найти другой шрифт, в утром есть все нужные вам иконки
    - Сделать такой шрифт самостоятельно. Это не так сложно. Вы просто находите SVG изображения социальных сетей, а потом на каком-то сервисе типа https://icomoon.io/, https://fontello.com/ (тут, похоже, уже есть нужные иконки) генерируете из этих иконок кастомный шрифт. И в CSS там где нужно показать иконку, указываете этот шрифт.
    Ответ написан
    Комментировать
  • Как организовать доступ к непубличным токенам?

    1. НИКОГДА не храните секреты (токены, логи-пассы и т.д.) в коде, который сохраняете в Git. Даже для пет-проектов. Даже если вы потом удалите этот токен из файла, он навсегда сохранится в истории гита, потом вы про это забудете, поделитесь кодом с кем-то, и он найдёт этот токен в истории. Вопрос не в том, найдет ли пользователь этот токен или нет, вопрос только в том, когда он найдёт этот токен. Очень-очень-очень многие просто забывают о том, что когда-то коммитили токен в репозиторий, забывают вычистить историю, и этот токен утекает...

    2. Код и секреты - это отдельные сущности, и жить они должны в разных местах. Если хотите поделиться кодом, обязательно опишите в README процесс получения токена, чтобы пользователи смогли хоть как-то этим кодом пользоваться. Это хороший тон в open-source.

    3. Если вы будете пользоваться системами CI/CD от Github или Gitlab, то там у репозиториев есть специальные разделы, в которых можно безопасно хранить секреты. Но вы должны быть внимательными, если сделаете кого-нибудь контрибьютером в вашем проекте. Не забудьте проверить все разрешения, которые выдаёте этим людям, потому что они при деплое проекта через эти системы CI/CD смогут вычитать ваши секреты. Если вы один хозяин репозитория, то хранить секреты там вполне безопасно.

    4. Если же вам всё-таки по какой-то причине очень надо, чтобы пользователи всё же как-то работали с вашим токеном, то единственное, что мне приходит в голову - это написать приложение типа API Gateway, которое будет имитировать ту апиху, от которой ваш токен. И это ваше приложение будет иметь базу данных пользователей которые смогут в вашем приложении регистрироваться и получать уже ваши токены, вами сгенерированные, для доступа к вашей апихе, а ваша апиха будет внутри себя вытаскивать данные из оригинальной API по оригинальному токену и отдавать эти данные им.
    Ответ написан
    Комментировать
  • Как реализовать домашний ПК для веб сервера и pet проектов?

    Если не боитесь использовать Cloudflare в наших волшебных политических реалиях, то у них есть прекрасное решение для обеспечения безопасности: "Zero trust tunnel". Это не просто прокси-сервер, а самый настоящий туннель. Т.е. ваш сервак может находиться за непробиваемым файрволом, но по туннелю вы сможете организовать безопасный доступ к тем ресурсам, к которым пожелаете.
    Таким образом, даже если кто-то будет атаковать ваши ресурсы, то атаковать они будут Cloudflare, а не ваш ненаглядный сервачок.
    Ответ написан
    Комментировать
  • Почему я получаю ошибку invalid operation: err (variable of type Error) is not an interface при проверке типа переменной?

    Я полностью присоединюсь к ответу выше, просто хочу дополнить.

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

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

    package main
    
    import (
    	"fmt"
    )
    
    type MyError struct {
    	Message string
    }
    
    func (e MyError) Error() string {
    	return e.Message
    }
    
    func main() {
    	// Возвращаем из функции нашу кастомную ошибку, но в виде интерфейса error
    	err := foo()
    
    	if err == nil {
    		fmt.Println("Нет ошибки")
    	// И теперь тут приводим error к нашему типу MyError и проверяем
    	} else if myErr, ok := err.(MyError); ok {
    		fmt.Printf("Ура! Нужный нам тип ошибки: %v\n", myErr.Message)
    	} else {
    		fmt.Println("Какой-то другой тип ошибки:", err)
    	}
    
    	// Проверка одной из "подстав" Go
    
    	err = bad()
    	if err != nil {
    		fmt.Println("Упс... Как так... Не nil...")
    	} else {
    		fmt.Println("Должно вывестись это, но не выводится...")
    	}
    
    	err = good()
    	if err != nil {
    		fmt.Println("Это не должно выводиться, всё верно.")
    	} else {
    		fmt.Println("Ошибки нет, всё верно.")
    	}
    }
    
    func foo() error {
    	err := MyError{"Ой! Ошибка MyError!"}
    	// err := fmt.Errorf("Ой! Обычная ошибка!")
    	// var err error = nil
    	return err
    }
    
    func bad() error {
    	var p *MyError = nil // Вроде же nil, но не работает....
    	// p = &MyError{"Ой!"} // Пробуем создать ошибку, и всё работает.
    
    	if p == nil {
    		fmt.Println("Ну nil же-ж... Должно же-ж работать", p)
    	}
    
    	return p
    }
    
    func good() error {
    	// return MyError{"Ой!"}
    
            // Буквально пишем "nil", никаких указателей, которые равны nil, это прямой выстрел в ногу
    	return nil
    }


    https://go.dev/play/p/2YcWcH9oqel
    Ответ написан
    Комментировать
  • Какой аналог itemOperations в api-platform 3 версии?

    Сам ещё не ставил третью версию, но одним глазом посмотрел документацию, и вроде как они тупо по названию классов в аргументе options ориентируются. В принципе, это логично, если подумать. Но не очевидно.
    #[ApiResource(
        operations: [
            new Get(), // item
            new Put(), // item
            new Patch(), // item
            new Delete(), // item
            new GetCollection(), // collection
            new Post() // collection
        ],
    )]
    Ответ написан
  • Есть ли готовая среда для работы с базой PostgreSQL через Веб?

    Ох, вот меня и ударило ностальгией по голове... 20 лет назад, где-то, делал то же самое. MS Access был тогда прекрасной штукой. Задача была такой же, и выбора не было. Я взял как раз свеженький PHP 5-й версии, и относительно быстро решил этот вопрос. И вам советую сделать так же.

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

    Поэтому, вам нужно знать магическое слово "CRUD" (Create, Remove, Update, Delete), и с этим словом выйти в интернет) В частности, можно ввести на YouTube что-то типа "crud за час", и вам выпадет огромный список видео. Там будут решения и на PHP, и на Python, и на других языках. Но PHP и Python - это то, что вам надо искать.

    Желаю вам всяческих успехов!

    P. S. Если не сможете в дизайн, закажите на фрилансе вёрстку ваших форм и списков за 20 копеек
    Ответ написан
    2 комментария
  • Как можно решить проблему с доступам к методам 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
    Ответ написан
  • Как решить зависимость класса от другого класса?

    У вас циклическая зависимость. Это первейший признак того, что вы что-то неправильно спроектировали. Выносите функционал, используемый обоими классами, в третий класс. Тогда вы избавитесь от циклической зависимости.
    Ответ написан
    Комментировать