• Есть ли паттерн для morph связи?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Морфы я бы сказал сами по себе такой костылек, легкий
    Обычно в теории бд есть связи один ко многим, а сущности каждая имеют свою таблицу.

    Морфа в данном случае нужна скорее сущности чтобы вытянуть ссылку только в случае если "тип равно сущность".

    С тем же успехом можно просто хранить ссылки и иметь таблицы привязки ссылок к сущностям отдельно

    Ссылки
    Видосы
    Картинки
    СсылкиВидосы
    СсылкиКартинки

    морф в данном случае выглядит так

    Ссылки
    Видосы
    Картинки
    СсылкиШтуки (type: видосы/картинки)
    (?) ТипыШтук(1. видосы, 2. картинки)

    По делу как с этим работать:

    Модели для каждой таблицы
    Класс-Команда "ПолучательШтукиПоСсылке"
    -- получить(ссылка)
    Класс-Команда "ПолучательШтукиПоИдИТипу"
    -- получить(ид, тип)
    Контроллер Линки
    -- ПолучательШтукиПоСсылке->получить(ссылка)
    Контроллер Картинки
    -- ПолучательШтукиПоИдИТипу->получить(картинка, ид)
    Контроллер Видосы
    -- ПолучательШтукиПоИдИТипу->получить(видос, ид)

    $link = Ссылки::with('image')->where('code', '123456myshortlink')->first();
    return $link->image;
    Ответ написан
  • Как разобрать зависимости без лямбда-замыканий, какая архитектурная ошибка допущена?

    gzhegow
    @gzhegow Автор вопроса
    aka "ОбнимиБизнесмена"
    Чуть позже обдумав - понял, что правило простое "если А зависит от Б, а Б зависит от А, нужно сделать В, от которого зависит и А и Б".

    То есть ситуация - валидатору нужен путь, чтобы проверять путь, а пути нужен валидатор, чтобы проверять что-то ещё. В этом случае валидатор умеет всё проверять, а это самое "что-то ещё" делает третий обьект. в котором на входе и валидатор и путь.

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

    устарело

    По проблеме с циклическими зависимостями найдено решение. Кому интересно напишите, расскажу весь путь как шли и почему так. Спасибо Андрей Ковалёв, Алексей Пастушенко, еще один Алексей, Александру Захаренко, Human с t.me@oop_ru канала, Александру Markwhide и Ярославу Кравцову, и еще двум парням в телеге

    1. Циклическая зависимость возникает когда нарушается принцип ООП - в зависимостях должны быть классы, которые меняются достаточно редко - а именно, когда происходит косяк или меняется задача. Не дополняется задача, а именно меняется то что было.

    2. Частным случаем такой ситуации является желание разместить все методы класса в одном файле по причине того, что они чем-то похожи. Это говорит о том, что класс будет часто меняться - когда человеку понадобится новый функционал похожий на старый, он должен открыть этот файл, дописать в него, и закрыть. Это говорит о том что файл меняется часто и не может быть зависимостью!

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

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

    5. В этом случае будет наблюдаться ситуация когда казалось бы независимые модули просят зависимости друг у друга, хотя друг о друге не знают. Первый шаг это воткнуть интерфейс на определенное действие в конструктор, а по интерфейсу подсовывать исполнителя.

    6. В пхп 7.3 ещё нельзя написать такой класс, который одновременно соответствует двум интерфейсам в двух разных модулях (как в фирме контракт нанимателя и контракт сотрудника) - таким образом приходится исходить из того что в рамках модуля зависимости можно переплетать. Есть способ - сделать обертку для класса и сунуть его зависимостью, а уже обертку повесить на собственный интерфейс, но тогда модулю нужен контейнер и конфиг который подсунет модуль в обертку параметром, и выдаст декорированный обьект. Если модулей несколько должно быть ядро, и код можно положить там. То есть модуль будет работать только с ядром. Либо делать компонент валидатора в каждом из модулей и копировать необходимые проверки, тогда модули можно безопасно выдергивать и вставлять. Да. Копирование кода.
    Ответ написан
  • Как это работает в symfony через Interface?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Я не отвечу тебе как оно внутри в симфони работает, но попробую добавить кое-что по интерфейсам

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

    С точки зрения управляющего зависимостями интерфейс это всего лишь название. Такое же как просто строка "апп.модульВася". Дальше сие пробросится через конструктор, который уже по логике ПХП проверит, то что было создано и подсунуто по этому названию - умеет ли то, что написано в интерфейсе? Нет? Ошибка. Кроме того сама пхп ещё до момента создания обьектов когда ты пишешь implements Интерфейс проверяет - умеет ли класс то, что он имплементит - тут как бы двойная связка

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

    Ты можешь и просто создать некий класс, приняв для себя что "пока что мне хватит его, менять я его не собираюсь" - таким образом модуль разбирающийся с тем какой класс от кого зависит - просто выполнит инструкцию new ЧтоТо() и подкинет параметры (рекурсивно) которые ты указал. Это могут быть вещи которые ты явно укажешь в конфиге, а могут быть точно такие же модули, как тот, который сейчас собирается.

    То есть управляшка зависимости создает обьект, читая его конструктор, и пытается по указанным типам данных подкинуть туда то, что можно создать. Что создать нельзя - она ругается, дескать - укажи это в конфигурации или передай вручную в момент создания. Но так как в симфони не принято создавать обьекты и обращаться к управляющему зависимостями напрямую - оно работает само, а если какой-то параметр заполнить нечем - ругается.
    Ответ написан
    Комментировать
  • Какой движок выбрать под корпоративный сайт с выбором городов?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    поддомены придется генерировать php-скриптом изменяя .htaccess или конфиг сервера, велика вероятность ошибки, но делают ведь
    каждый поддомен это как минимум сервер или хотя бы алиас имеющегося. сервер должен знать что на поддомене что-то есть. сначала реализуй это через уровень вложенности ссылки. а потом подключи к этому перестроение конфига сервера типа "построил новый, сохранил старый, взаимно изменил имена, перезагрузил сервер"
    Ответ написан
    8 комментариев
  • Как сделать сокращение числа?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    количество раз когда после деления на тысячу число получается больше единицы следует прогнать через ключ массива - 3 раза поделилось - значит у нас миллиарды
    Ответ написан
    Комментировать
  • Как выбрать нужные данные по ключу из многомерного массива / коллекции?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    блин помоему input вернет тебе array, и обращаться нужно как к array
    опять же обязательно должен быть заголовок Content-Type:application/json, иначе input вернет ничего, т.к. по его мнению пришла строка
    Ответ написан
    Комментировать
  • Как сделать адаптив для этого элемента?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    В смысле как? Это не кнопочкой делается, это берешь media запросы и пишешь.
    Или хотя бы сетку bootstrap, и верстаешь 5 блоков
    Есть флексы ещё но я их не очень люблю, вот эти все ихние "флексить йоу кулл"
    Ответ написан
  • Как мне настроить работу с git для prod и dev сайта?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Тщательно изучи тему CI/CD в любом поставщике гита. Это как раз и есть автоматическое развертывание после новых пушей. Придется добавить мерж-реквесты, чтобы кто-то из команды случайно не залил свои ошибки без твоего подтверждения его работы.

    Если тебе лично для себя, то можешь просто использовать ветки
    git checkout -b dev-zadacha1 (создает ветку (папку) и переходит на работу в ней)
    git add . (добавить все измененные файлы в снимок - точнее изменения из файлов с момента прошлого снимка)
    git commit -m "комментарий" (сфотографировать и добавить в папку на ТВОЕМ компьютере)
    git push -u origin dev-zadacha1 (закачивает на СЕРВЕР гита изменения (по умолчанию сервер маркируется словом origin), создавая там папку и связывая твою папку и папку на гите)

    теперь все последующие изменения делаются
    git add .
    git commit -m "комментарий"
    git push

    когда ты будешь уверен, что штука готова окончательно
    git checkout master (отсутствует `-b master` - создавать папку master не надо - она по умолчанию есть - перейти в папку master, не пугайся что весь проект вдруг откатится к старой версии, изменения лежат в папке .git на компьютере)
    git pull (скачиваем последние изменения для мастера, которые делали коллеги)
    git merge dev-zadacha1 (соединить текущую папку с dev-zadacha1, путем слияния изменений построчно) - в этом месте тебе придется разобраться с конфликтами, если ты что-то делал в папке мастер с теми же самыми файлами, что и в папке dev-zadacha1.
    git push -u origin master (заливаем в папку мастер на сервер и говорим гиту что дальше нужно следить за папкой мастер)
    Ответ написан
    Комментировать
  • Как отправить письмо на эл. почту средствами php по времени?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Твой принцип работы в этой задаче
    1. создать список с планом отсылки и добавлять туда записи через скрипт 1.
    2. создать скрипт 2 который берёт первый десяток и списка и смотрит, наступило ли время отсылки, затем отправляет те, что уже должны быть отправлены
    3. запускать этот скрипт по планировщику (cron) или через механизм очереди - в этом случае пункты 1 и 3 обьединяются, т.к. стоит тебе сделать 1, как мгновенно выстрелит 3 в отдельной программе никак не знающей о том, кто и когда поставил новую задачу
    Ответ написан
    Комментировать
  • Почему сбрасывается соединение при выполнении php?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Ещё если ты работаешь с прокси частая проблема в том что круто настроенный под скорость nginx обрывает долгие запросы в ошибку 502 (долгие иногда фанатики считают - это 3 секунды). Стоит поиграть с конфигом nginx тоже.

    Если дело в самой проксе и она падает в ошибку, то перед запуском основного запроса рекомендую сделать запрос на какую-нибудь апишку гугла, которая заведомо вернет тебе json что в запросе косяк. Наша задача проверить - вернёт или нет. Если больше секунды выполняется запрос на апишку гугла, то проксю следует поменять на следующую по списку и повторить.
    Ответ написан
    Комментировать
  • PHP. Возможно ли добавление контента без Базы Данных?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Любую программу стоит рассматривать в отрыве от базы данных

    Это значит что во время разработки твой контент страниц находится прямо в коде и программа должна заработать. А потом в те места где был контент ты вставляешь функции типа getContent(), которые в своём коде забирают информацию оттуда, где она сейчас размещается. В первую очередь проверяются скоростные варианты - оперативная память (переменные) или redis (кэш), потом файловая система (кэш), потом база данных, расположенная на данной машине, потом возможно из локальной сети, потом базы данных на удаленных серверах амазона или гугла, в конце - апишки.

    Разумеется, не нужно размещать её везде. Отталкивайся от этого приоритета и того, на что готова пойти нагрузка, ты и руководство компании. В идеале все бы хранить в оперативки, да её не хватит. Всё хранить на диске? Так это обычно HDD, SSD диски больших размеров денег стоят. Всё бы хранить в базе данных на этой машине? Так это минус половина оперативки на работу с запросами (обычно этот вариант для приложений поменьше), в случае предела нагрузки проект станет колом и будет валиться в ошибки постоянно, мгновенно не исправить, нужно покупать железо например. Базы на удаленных серверах амазонов или гуглов - это во первых постоянный платеж, во вторых - пинги на запросы. Апишки - это время на запрос, плюс написание оберток на эти апишки.

    Можно базу разместить где-то в локальной сети на другой машине, чтобы пинг минимальный, или если нагрузки бешеные - тогда сразу идут в амазоны. Основное преимущество - возможность с помощью "бегунка" увеличить размер базы или количество памяти просто увеличив ежемесячный платеж за этот компьютер без дозакупы железа. Чтобы минимизировать пинги - делают кеширование в оперативку того, что ну очень часто нужно и редко меняется.

    Одновременно не забывают продумать систему очистки кеша. Ведь когда ты меняешь запись - контент должен измениться, а если кеш останется старый - то будут вопросы. Ты конечно скажешь что "скоро оно поменяется", но бизнес не любит таких отмазок. И очень любит повторять один и тот же вопрос, ожидая на него почему-то другой ответ. Если они повторяют свой вопрос несколько раз, а ты отвечаешь одинаково - тебя увольняют.

    Вот и решай.
    Ответ написан
    Комментировать
  • PHP обфускатор Как запустить?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Для потомков:

    > php obs.php obs.php obs2.php
    > php obs2.php obs.php obs3.php


    <?php
    
    define('ERR_BAD_EXTENSION', 'ERR_BAD_EXTENSION');
    define('ERR_BAD_USAGE', 'ERR_BAD_USAGE');
    define('ERR_EMPTY_FILE', 'ERR_EMPTY_FILE');
    define('ERR_EXISTS_FILE', 'ERR_EXISTS_FILE');
    define('ERR_NOT_FOUND_FILE', 'ERR_NOT_FOUND_FILE');
    
    $messages = [
    	ERR_BAD_EXTENSION  => 'Incorrect file extension',
    	ERR_BAD_USAGE      => sprintf('Usage: php %s <input file> <output file>', basename(__FILE__)),
    	ERR_EMPTY_FILE     => 'File name should be not empty',
    	ERR_EXISTS_FILE    => 'File exists',
    	ERR_NOT_FOUND_FILE => 'File not found',
    ];
    
    function _error($err, $subject = null)
    {
    	echo PHP_EOL . $GLOBALS[ 'messages' ][ (string) $err ] . PHP_EOL . print_r($subject, 1);
    	die();
    }
    
    function _log($message, $subject = null)
    {
    	echo PHP_EOL . (string) $message . PHP_EOL . print_r($subject, 1);
    }
    
    if (! isset($_SERVER[ 'argv' ][ 1 ])) {
    	_error(ERR_BAD_USAGE);
    }
    
    if (! isset($_SERVER[ 'argv' ][ 2 ])) {
    	_error(ERR_BAD_USAGE);
    }
    
    $from = $_SERVER[ 'argv' ][ 1 ];
    $to = $_SERVER[ 'argv' ][ 2 ];
    
    if (! $from) {
    	_error(ERR_EMPTY_FILE, $from);
    }
    if (! is_file($from)) {
    	_error(ERR_NOT_FOUND_FILE, $from);
    }
    if ('php' !== ($ext = pathinfo($from, PATHINFO_EXTENSION))) {
    	_error(ERR_BAD_EXTENSION, [ $from, $ext ]);
    }
    if (is_file($to)) {
    	_error(ERR_EXISTS_FILE, $to);
    }
    if ('php' !== ($ext = pathinfo($to, PATHINFO_EXTENSION))) {
    	_error(ERR_BAD_EXTENSION, [ $to, $ext ]);
    }
    
    _log('Processing...',
    	[
    		$from,
    		$to,
    	]
    );
    
    $content = ''
    	. 'ob_end_clean(); ?>'
    	. php_strip_whitespace($from)
    ;
    
    $data = ''
    	. '<?php' . PHP_EOL
    	. sprintf('$a=\'%s\';', base64_encode(gzcompress($content, 9)))
    	. 'ob_start();'
    	. 'eval(gzuncompress(base64_decode($a)));'
    	. '$v=ob_get_clean();'
    ;
    
    file_put_contents($to, $data);
    
    _log('Done.', $to);
    Ответ написан
    Комментировать
  • Все значения массива в нижний регистр php?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    В пхп недостаточно удобных "стандартных" функций (в том смысле что хотелось бы больше)

    Если предлагать у них на трекере - они защищаются тем, что тебе нужно подымать голосование RFC чтобы много людей проголосовали "за"

    На текущем уровне разобщенности люди не способны за кого-то голосовать, а шанс что они столкнуться с одинаковой проблемой и тем более осознают что это проблема - вообще никакущий - это заморозило язык, они разрабатывают сейчас проверку типов по интерфейсу в 7.4 и прекомпиляцию исходников, но не делают язык удобнее для тех, кто ещё не освоил ООП)

    Очень часто язык предлагает не лениться писать foreach

    $result = array_map('mb_strtolower, array_map('trim', $arr));
    var_dump($result);


    или

    // передаем по ссылке чтобы заменить на любом уровне вложенности прямо в исходном массиве
    array_walk_recursive($arr, function (&$v) {
      $v = mb_strtolower(trim($v));
    });
    var_dump($arr);
    Ответ написан
    Комментировать
  • Как в Laravel 6 добавить к Route:: языковой префикс с поддержкой языка "по-умолчанию"?

    gzhegow
    @gzhegow Автор вопроса
    aka "ОбнимиБизнесмена"
    сегодня какимто чудом бубном и такойто матерью разобрался

    Route::pattern('locale', '|ru/|en/');
    Route::get('{locale}route', 'action');
    // /(?P<locale>(?:|en/|ru/))route


    Потом чтобы поставить локаль отрезаем правую наклонную. Так я когдато на чистом пхп делал, так и здесь прокатило, иначе получалось то две косые, то ни одной

    Чтоб сделать редирект после сравнения с кукой или сессией косую надо вернуть на место
    Ответ написан
    Комментировать
  • Как убрать X-Redirect?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    X- префикс значит что заголовок сделан вручную, а не отдан сервером, никакой магии
    Какой-то плагин думает вместо тебя

    Причем вручную можно сделать и просто заголовок. Это вроде как соглашение - делаешь свой - делай X-
    Ответ написан
    Комментировать
  • Как скачать динамически созданный файл и сделать location этой страницы?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    к сожалению только яваскрипт. ты не можешь отправить файл, а потом сделать редирект. отдача файла это код 200 - все ок. а редирект это 301 код

    логика действий - попробовать сделать как на megashare помоему или какой-то такой известный обменник который отдает файл своим способом, я полагаю он как-то его по кускам передают яваскрипту через вебсокет наверное, а потом соединяют куски файла уже на клиенте и скачивают как бы мгновенно. и после этого мгновенного скачивания - перенаправить человека
    Ответ написан
    Комментировать
  • Как сделать что бы Laravel API Возвращало только json?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    <?php
    
    namespace App\Http\Middleware;
    
    class Api
    {
    	public function handle($request, $next)
    	{
    		$request->headers->set('Accept', 'application/json');
    
    		return $next($request);
    	}
    }
    Ответ написан
    2 комментария
  • Микросервис простым языком?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Понятие позволяющее разбить проект на людей, которые каждый пишут на своем языке.
    Смысл сводится к тому, что программы обмениваются данными через интернет, а не запускаются в рамках одного компьютера - соответственно пропадает понятие классы-интерфейсы-неймспейсы, появляется понятие "запрос-ответ-косяк" :)

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

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

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

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

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Еще можно вызвать array_combine([id,name], array_map(null, $result)) и получить вместо
    [
      0 => [ id => 1, name => 'vasya' ],
      1 => [ id => 2, name => 'petya' ],
    ]


    массив
    [
      id => [ 1,2 ],
      name => [ vasya, petya ],
    ]


    суммируй потом, фильтруй, что хош делай
    Ответ написан
    Комментировать
  • Для чего используют функции serialize/unserialize?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    ещё чтоб сделать файловый кеш тебе нужно сохранить данные, время жизни и дату сохранения. сами данные в serialize, а массив из трех полей - в json. иначе было бы дважды json и велика вероятность потери
    Ответ написан
    Комментировать