Pull to refresh
2
0
Sway @Sway

User

Send message

Не надо так делать! Некромантия в программировании до добра не доводит. Тем более есть актуальный Laravel Echo, который совместим с бесплатным websocket сервером Soketi. Вот доки: https://docs.soketi.app/getting-started/client-configuration/laravel-echo. Работают они друг с другом отлично, нужно только разобраться с самим Echo. И никакой некромантии не нужно.

Мне лень статьи писать =) Я даже комментирую редко.

Как по мне, то если на собеседовании загоняют в такие рамки и не дают использовать привычный или хотябы схожий набор инструментов, то возникает вопрос об адекватности собеседующих. Отнимите в ответ у собеседующего бумажку/планшет/ноут по которым они вопросы задают. Пусть почувствуют себя на Вашем месте =)))

Давайте тогда представим что я тот кто оценивает Ваше решение =)
У меня возникают следующие вопросы:

  1. Почему использовался контекст? (Ответ уже знаем: чтобы не прокидывать пропсы)

  2. Чем Вас не устраивает прокидывание пропсов в данной задаче? Ведь это было бы проще и понятнее. Да и компонент же довольно простой, а не конструктор типа Dropdown Menu где контент передается извне и нужно иметь возможность взаимодействия с верхним уровнем.

  3. В проекте где есть рейтинг чаще всего будет и избранное, которое идеально ложится на функционал компонента Star, что будете делать?

  4. Почему не используется TypeScript? Он бы почти никак не повлиял на скорость решения задачи, зато была бы типизация хорошо прописана и соблюдена.

Посудите сами - данное тестовое задание дается не только чтобы проверить что Вы можете написать компонент или знаете какие-то особенности технологии/инструмента/языка, но и то как Вы это сделаете, какие возможности и как примените, и как Вы оцениваете ситуацию вне задачи (вопрос 3).
Я ничего не имею против контекстов, прекрасная штука, но тащить их только ради того чтобы не прокидывать пропсы - это как-то неэффективно ни по времени ни по результату. Более того контекст обязывает все компоненты его использующие быть зависимыми друг от друга. Уже нельзя будет для вопроса 3 взять готовое решение. Нужно будет переделывать. А это время и деньги. И не нужно говорить что этого небыло в задании. В реальности далеко не всегда заранее известно какой компонент будет переиспользоваться в будущем. Т.е. я как наниматель увидел что Вы смогли решили задачу, но не подумали о том что некоторые компоненты можно будет переиспользовать в будущем. Ведь у компонента Star очень широкое применение. Его и в like можно было бы превратить всего-лишь поменяв иконку. Но увы, придется делать отдельный компонент...

Я, если честно, не понял зачем так сильно усложнять практически всё. Разве что ради того чтобы показать что ты умеешь в контексты. Только если контексты так использовать на каждый чих, то не выльется ли это в проблемы в итоге? Как по мне так в данном случае использование контекста - это misuse. Например компонент Star в каком-то другом месте использовать будет невозможно. Что совсем не круто.
Почему не используется TypeScript?
И, кстати, самое тормозное никак не оптимизировано - иконка. Представьте страницу на которой очень много звёздочек, а иконка каждой звездочки - отдельный svg элемент с одинаковым path. Это прямо очень тормозное дело, особенно если иконка в каждом блоке со компонентом рейтинга не одна. А ведь далеко не все иконки имеют такой короткий path. Я напоролся на такую проблему и скажу что абсолютно все остальные оптимизации - экономия на спичках. У меня в одном блоке было всего 4 разных иконки (разные, с длинным path), но блоков было много - от сотни до нескольких тысяч. В каждом блоке было от 4 до 12 иконок (чекбоксов могло быть от 1 до 8 в разном виде) Так вот - браузер очень туго переваривал весь этот зоопарк иконок. На довольно мощном компе.
Решение: в svg есть возможность клонирования/переиспользования элементов тэгом <use>.
Т.е. нужно:

  1. Вынести иконку в отдельный компонент

  2. При первом создании компонента пробрасывать полный svg в <body> или в другое место откуда будет максимально удобный доступ (Пример полного svg: <svg><circle cx="50" cy="50" r="10" fill="red" id="primcirc" /></svg>, обратите внимание на id). Тут особенность в том что нельзя чтобы полный SVG уничтожался при последующих перерисовках, иначе пропадут все иконки-клоны.

  3. При следующих рендерах компонента выводить не полный svg, а <use xlink:href="#primcirc" /> Подробнее гуглите: Cloning SVG Elements: Using use

Вот полный работающий код компонента иконки на основе MDIIcon (Material Design Community Icons):

import * as React from "react";
import {AllHTMLAttributes, CSSProperties, useEffect, useRef} from "react";

let idCounter: number = 0;
let reusableIcons: {
    [key: string]: boolean
} = {};

export interface IconProps extends Omit<AllHTMLAttributes<SVGSVGElement>, 'size' | 'label'> {
    id?: string,
    path: string,
    ref?: React.RefObject<SVGSVGElement>,
    title?: string,
    description?: string | null,
    horizontal?: boolean,
    vertical?: boolean,
    rotate?: number,
    spin?: boolean | number,
    style?: CSSProperties,
    size?: number | null,
    reuse?: string, //< icon id to be reused
    reusableItemsContainerJquerySelector?: string | 'body'
}

export default function MDIIcon(props: IconProps) {
    const {
        id,
        path,
        title,
        description,
        size,
        horizontal,
        vertical,
        rotate,
        spin,
        reuse,
        reusableItemsContainerJquerySelector,
        style: customStyle = {},
        ...rest
    } = props;

    const ref = useRef<SVGSVGElement>(null);

    useEffect(() => {
        if (reuse && ref.current && ref.current.getAttribute('data-use') === '0') {
            // copy original svg element to <body> so that it will persist there even after rerender
            // on rerender original svg element will be replaced by <svg><use href="#{reuse}"/></svg> and if
            // there are no clone inside <body> - all <use> tag will target nothing and icons will not be rerendered
            reusableIcons[reuse] = true;
            const existing = document.getElementById(reuse);
            if (existing) {
                window.$(existing).parent().remove();
            }
            const $container = window.$('<div style="display: none;" class="react-app"></div>')
                .append(window.$(ref.current.cloneNode(true) as any).attr('id', reuse));
            if (reusableItemsContainerJquerySelector === 'body') {
                window.$(document.body).append($container);
            } else {
                const $supercontainer = window.$(document.body).find(reusableItemsContainerJquerySelector as string);
                if ($supercontainer.length !== 0) {
                    $supercontainer.append($container);
                } else {
                    const message = '[MDIIcon] failed to find element for reusableItemsContainerJquerySelector = ' + reusableItemsContainerJquerySelector;
                    console.error(message);
                    window.$(document.body).append($container);
                }
            }
        }
    }, [ref.current, reuse])

    const index = ++idCounter;
    const pathStyle: any = {};
    const transform = [];
    const style = Object.assign({}, customStyle || {});
    if (size !== null) {
        style.height = style.width = size + 'px';
    }
    if (horizontal) {
        transform.push("scaleX(-1)");
    }
    if (vertical) {
        transform.push("scaleY(-1)");
    }
    if (rotate !== 0) {
        transform.push(`rotate(${rotate}deg)`);
    }
    let transformElement = (
        <path
            d={path}
            style={pathStyle}
        />
    );
    if (transform.length > 0) {
        style.transform = transform.join(' ');
        style.transformOrigin = 'center';
    }
    let spinElement = transformElement;
    const spinSec = spin || typeof spin !== 'number' ? 2 : spin;
    let inverse = horizontal || vertical;
    if (spinSec < 0) {
        inverse = !inverse
    }
    if (spin) {
        spinElement = (
            <g
                style={{
                    animation: `spin${inverse ? '-inverse' : ''} linear ${Math.abs(spinSec)}s infinite`,
                    transformOrigin: 'center'
                }}
            >
                {transformElement}
                {!(horizontal || vertical || rotate !== 0) && (
                    <rect
                        width="24"
                        height="24"
                        fill="transparent"
                    />
                )}
            </g>
        )
    }
    let ariaLabelledby;
    let labelledById = `icon_labelledby_${index}`;
    let describedById = `icon_describedby_${index}`;
    let role;
    if (title) {
        ariaLabelledby = description
            ? `${labelledById} ${describedById}`
            : labelledById;
    } else {
        role = 'presentation';
        if (description) {
            throw new Error("title attribute required when description is set");
        }
    }
    if (reuse && reusableIcons[reuse]) {
        return (
            <svg
                ref={ref}
                viewBox="0 0 24 24"
                style={style}
                role={role}
                aria-labelledby={ariaLabelledby}
                data-use="1"
                {...rest as any}
            >
                <use href={'#' + reuse}/>
            </svg>
        );
    } else {
        if (reuse) {
            reusableIcons[reuse] = true;
        }
        return (
            <svg
                ref={ref}
                viewBox="0 0 24 24"
                style={style}
                role={role}
                aria-labelledby={ariaLabelledby}
                id={id}
                data-use="0"
                {...rest as any}
            >
                {title && <title id={labelledById}>{title}</title>}
                {description && <desc id={describedById}>{description}</desc>}
                {spin && (
                    inverse
                        ? <style>{"@keyframes spin-inverse { to { transform: rotate(-360deg) } }"}</style>
                        : <style>{"@keyframes spin { to { transform: rotate(360deg) } }"}</style>
                )}
                {spinElement}
            </svg>
        );
    }
}

MDIIcon.defaultProps = {
    horizontal: false,
    vertical: false,
    rotate: 0,
    spin: false,
    size: 24,
    style: {},
    reusableItemsContainerJquerySelector: 'body'
} as Partial<IconProps>;

Извините за использование jQuery. Проект был на него завязан, а я не настолько ханжа чтобы этим не воспользоваться.
Я не претендую на идеальность кода или решения, но это единственный вариант который я нашел и который достаточно хорошо работает имея минимальные недостатки (по сути только лишний код в <body>). Не стоит пользоваться этой функцией везде - оно имеет смысл только если одинаковых иконок очень много (примерно от 50-100 штук на каждую иконку).

Где как раз сложные формы неплохо работают в связки полей и т.п.. в том числе где нет необходимости поддержания реактивого вывода данных с использование асинхронного взаимодействия пользовательских интерфейсов с системой обработки данных.

В моем понимании это как раз и есть сложные формы =) Т.е. форма в которой, например создается родитель и дети, например - опрос с вариантами ответов. Знаю, вариант довольно простой, но бывают даже такие с кучей настроек как у опроса так и у вариантов ответов. Я рассматривал LiveWire как альтернативу, но решил что React меньше будет сервер грузить, да и опыт у меня с ним уже был неплохой. Вообще мне понравилось когда сервер - это просто API, а фронт как бы отдельно от этого всего. Есть в этом что-то правильное.

Использовать фреймворк не имеющий нормальной поддержки и не получившей её за 4 года существования равносильно выстрелу в голову для руководства. Каким бы ни был прогрессивным фреймворк - если его не используют массово, то он никому не нужен. Ну и синтаксис шаблонов - это отдельный трэш. Шаблоны невозможно понять быстро - они почти полностью нечитабельны. Соответственно нужно вникать в каждую строку и расшифровывать бесконечные спецсимволы, даже если их всего несколько. Это контрпродуктивно как для разработки, так и для поддержки. В добавок нет поддержки со стороны IDE что еще больше усугубляет ситуацию. Я оценил фичи, некоторые действительно крутые, но поймите - как бы Вы не пиарили фреймворк, если его разрабатывает пара человек в свободное время без поддержки со стороны бизнеса, то использование такого фреймворка имеет высокие риски остаться с непопулярным фреймворком наедине. И им придется переписывать проект на что-то другое. Высокие риски допустимы для некоторых стартапов, но не для более-менее крупных компаний. И стартапы тоже предпочтут React/Vue/Angular т.к. у всех у них есть много документации, экосистема, сообщество, разработчики, которых можно нанять. Где вы найдете разработчиков знающих или готовых работать со $smal на замену ушедшему? На рынке сейчас итак недостаток качественной рабочей силы, а тут еще и неизвестный малопопулярный фреймворк. Да все качественные кандидаты просто пойдут к конкурентам после того как ознакомятся со $mol и Вашими статьями в стиле "всё вокруг говно, а $mal лучше всех!". Может и лучше, но почему-то никому не нужен...

Вот скорее всего допилили поддержку. Я не помню конструкций вида `lang="ts" ` и `script setup`.

К сожалению уже нет конкретного примера. Было это год назад и после того как понял что дело так дальше не пойдет, я удалил всё что там было. Если правильно помню, то проблема там была в том что практически все сторонние либы под Vue не дружили с TS, что делало использование TS болью из declare module. Еще какая-то беда была с первоначальной настройкой TS. Глянул сейчас документацию по активации TS - мне кажется это довольно сложным даже сейчас, когда я намного больше знаю про настройку TS и webpack. С реактом всё как-то сильно проще было.

Только вот огромные формы со сложными зависимостями полей друг от друга сильно проще делать как раз на Vue/React, а не на чистом js или js + dotJs, например. И дело не в визуальной части а именно в функциональной.

Я на своем проекте оценил насколько проще, быстрее и функциональнее стали формы после переделывания их на React. Это не повод, конечно, специально переделывать то что есть если оно работает и не требует серьезных изменений. Но новый функционал можно довольно эффективно внедрять в уже существующий проект. Если правильно настроить webpack.

Зайти однозначно проще. Я быстро зашел. Но потом так же быстро вышел т.к. Vue 3 + Typescript почти невозможное сочетание. А без Typescript - зачем вообще так жить? После довольно долгих мучений я таки подружил его с Typescript, а потом напоролся на то что часть сторонних библиотек не обновлена под Vue 3 и полностью с ним несовместима. Мне, например, не улыбается самостоятельно велосипедить Bootstrap компоненты, но пришлось бы если б остался. После прототипирования пары страниц я понял что работая с Vue я превозмогаю его, а не использую с удовольствием. На этом я ушел в React и настроил всё что мне нужно было намного быстрее, с меньшей болью и усилиями. Даже удовольствие получил от того что оно "просто работает". Typescript из коробки доступен, всё прекрасно работает в IDE со всеми возможными подсказками включая стили. Добавить react router, i18n, transition и набор бутстраповских компонентов на выбор - и можно работать. Для react есть практически все возможные либы и многие из них поддерживаются на свежих версиях реакта, т.е. есть вполне живая экосистема. Для vue 3 экосистемы за пределами стандартных либ можно считать что нет. Такое чувство было что многие решили не обновлять либы с Vue 2 на Vue 3 из-за довольно объемных изменений в ядре Vue 3. Единственное что мне прямо очень понравилось во Vue - как красиво и удобно реализовано глобальное состояние. Redux, часто используемый в паре с React - это мозговыносящая переусложненная боль прямо. Может для супер больших online+offline проектов оно и оправдано, но у меня проект довольно средненький и я предпочел Modx - как-то он логичнее и понятнее. Тем не менее лучшее решение из тех что я пробовал именно у Vue. Хотя в общем я согласен с автором что React слишком либерален и для совсем кривых рук не подходит - там нужно понимать что ты делаешь. Те же хуки - это прямо кладезь потенциального технического долга. Но при этом и очень мощный инструмент при правильном использовании.

9ка на мой взгляд ставила основной целью переход на PHP 8.0, добавление типизаций и аннотаций, а так же обновление компонентов symfony которые начали требовать PHP 8.0.

Всё остальное - так, для вида что что-то таки добавили. Хотя вот допиливание страницы ошибки уже как-то надоело, особенно в условиях когда она адски тупит и не работает при ajax запросах. Я давно отказался от нее как раз потому что ждать пока оно отрендерится слишком долго, иногда исправление ошибки занимало меньше времени чем ожидание вывода информации об ошибке. В итоге использую намного более простое решение в виде своего "костыля" (если интересно - swayok/laravel-extended-errors) + отправку в этом же формате себе в Telegram канал =)

Эта магия настолько черна что даже "неназываемый" ею брезгует =)))

Не знаю, Twig не использовал. Но он не нативен для Laravel как минимум. Я уж лучше тогда Vue на фронте прикручу — пользы больше будет. Вариант перехода на Symphony не рассматривается — слишком муторно.

Да вот, похоже, пора. Когда-то давно я хотел дождаться большей функциональности и в итоге забыл о нем =)

Оно вполне удобное. Смысла его использовать для простых страниц мало, это да. Но чуть что посложнее — либо он, либо уходить полностью во frontend (vue, react, angular и т.п). Кому что удобнее. Делать свои костыли — не вижу смысла, это бесполезная трата времени (у меня есть несколько таких костылей и этого более чем достаточно чтобы трезво оценивать необходимость новых). Прикручивать к Laravel чужие костыли — а зачем? Есть Blade, он работает достаточно хорошо, но хромает поддержка в IDE.

Понимаю что сложно, но надо как-то решать, ато вот вообще больно стало этим всем пользоваться.
Может на основе настроек Blade директив из настроек IDE конвертировать код в чистый html c php вставками и уже анализировать его?


Это так же позволит настройками Blade директив внедрять специальные переменные типа $loop в итераторы @for, @foreach и т.п. Пример настройки Suffix для each директивы: ); /** @var \stdClass $loop */ ?>

Довольно толковый релиз получился. Молодцы.


А теперь ложку дегтя:
Когда завезут нормальную поддержку Blade шаблонов? Этот вопрос висит уже много лет и нет никакого развития в эту сторону. Скорее наоборот — с каждой версией IDE становится всё хуже. Самая боль — подсказка имен переменных работает только в редких случаях. Например, если в шаблоне через phpdoc указать входящие переменные, то в Blade директивах нет подсказки имен к ним, но после того как полностью введено имя переменной, IDE его кое-как сопоставляет с тем что определено в PHPdoc (через Ctrl видно тип и даже начинает работать подсказка свойств объекта если переменная — объект), но как бы это уже не особо полезно. Аналогичная проблема с блоками @php и <?php ?> — они вообще живут в разных областях видимости даже относительно остальных Blade директив. Хотелось бы чтобы поддержка Blade шаблонов была более адекватной, чтобы не приходилось варить кашу из костылей типа такого:


<?php
/**
 * @var array $array
 */
?>
@foreach($array as $value)
    @php(/** var array $value */ $varname = array_get($value, 'key', 'default'))
    {{ $varname }}
    <?php echo $array . $value . $varname ?>
@endforeach

Тут проблема чуть ли не в каждой строке:


  1. Определенную в PHPdoc переменную $array не видно в Blade директивах, соответственно никаких подсказок имени в @foreach или в других блоках
  2. В блоке @php() не видно переменную $value настолько что IDE выдает там ошибку 'Undefined variable $value' если не прописать /** var array $value */
  3. В блоке @php() при прописанном /** var array $value */ ошибки нет, но есть предупреждение 'Expresion result is not used anywhere' хотя на следующей строке переменная используется
  4. в строке {{ $varname }} IDE не знает о переменной $varname, т.е. и тут нет подсказки имени
  5. в строке <?php echo ?> IDE видит $array, но $value и $varname подсвечивает с ошибками 'Undefined variable $value' и 'Undefined variable $varname'

Как видите — пользоваться такой реализацией совершенно не удобно. Это сплошная борьба с IDE вместо того чтобы заниматься чем-то полезным. В целом эффективность написания Blade шаблона в блокноте не сильно ниже чем в PHPStorm. Надеюсь Вас это заденет.


На все эти косяки есть Issue в багтрекере, которые игнорируются годами. Ну тут ничего нового — в JetBrains это уже стало традицией =( Как и то что я к каждому релизу на что-то жалуюсь на Хабре =)

Далекие времена PostgreSQL 9.1. Я тогда настройки делал по инструкциям. Магии как с настройками MySQL не припомню. Тем не менее около 2х лет проект работал без нареканий на БД. Что было после — не знаю.
Сейчас настройкой занимается специально обученный человек, так что не совсем в курсе насколько всё изменилось.
Мне казалось наибольшей сложностью перехода с MySQL на PostgreSQL для большинства было незнание стандартного SQL и привычка использовать нестандартные фишки MySQL. В добавок более строгая типизация не позволяла делать всякую фигню как в MySQL без явного приведения типов. А еще в PG foreign key уже тогда нормально работали в отличие от MySQL. Это как из песочницы вылезти в реальный мир.
А еще в PG нет вечного выбора между MyISAM и InnoDB.
Для меня наоборот после перехода на PG всё встало на свои места т.к. в универе был весьма толковый курс по стандартному SQL.

Как я Вас понимаю =) Я лет 10 назад после очередного марафона по оптимизации настроек и запросов в MySQL обнаружил что это недоразумение отказывается использовать выделенные ему 16 гигов оперативки и использует HDD (скажем спасибо TEXT типу колонок). В итоге психанул и перевел проект на PostgreSQL с минимальными изменениями в структуре и типах полей. В итоге мало того что все проблемы с нагрузкой испарились, так я еще и не настраивал в PG ничего кроме разрешенного объема памяти. Тогда еще были проблемы с производительностью COUNT(*), но проект все-равно работал намного быстрее чем MySQL. А потом я начал вспоминать универ и стандарт SQL со всеми его возможностями, которыми в MySQL и не пахло. В общем с того момента я зарекся использовать MySQL в проектах более чем сайт-каталог с небольшим количеством данных.
На сколько я понял — проблема с непопулярностью PostgreSQL в те времена была в основном потому что мало кто знал о нем и мало кто был способен адекватно использовать его возможности. Я вот, например, кайфовал от количества типов колонок и строгости типизации данных в сравнении с MySQL. А многие считают это проблемой или не понимают как это правильно использовать.
А еще больше меня удивляет скудность поддержки PostgreSQL в фреймворках и ORM даже сейчас.

1
23 ...

Information

Rating
Does not participate
Location
Украина
Date of birth
Registered
Activity