All streams
Search
Write a publication
Pull to refresh
63
0.5
Михаил @michael_v89

Программист

Send message
это как раз в варианте с БЛ на сервере нет этих проблем

И поэтому вы написали целую статью, как сделать безопасность на уроне строк?) В приложении в простом случае это делается тривиально if ($entity->user_id == $currentUser->id), в сложном через RBAC if ($user->can('viewEntity', ['entity' => $entity]))

Когда при загрузке? Если в момент загрузки страницы, то так не пойдёт, т.к. пока пользователь на странице oldAttributes могут измениться. Так что придётся считать перед изменением и ещё заблокировать строку пока вы на клиенте вычисляете остальные значения полей, чтобы в этот период никто не изменил строку.

В момент загрузки данных сущности при обработке запроса. Да, это обеспечивается блокировками и транзакциями, они для того и придуманы, это не очень сложно сделать. MySQL делает примерно то же самое, чтобы обеспечить атомарность и последовательность запросов.

а это существенная просадка производительности

А вы проверяли? Давайте проверим. Насколько я понимаю, у вас есть возможность сделать тестовую базу с данными и триггерами. Допустим, вы бы могли выложить такую базу на github, и привести пару примеров, на которых можно проверить производительность. А я бы попробовал написать приложение, которое делает то же самое. Можно будет проверить разницу и решить, стоит ли она того, чтобы переносить бизнес-логику в базу.

прайс, скидка клиента общая, скидка клиента по производителю, скидка клиента по товару, курс валюты. В итоге на вставку одной позиции документа надо поочерёдно сгенерить 5 запросов и обменяться ими php и mysql'ю.

Редко меняющиеся сущности, для них как раз можно использовать кеширование и не дергать базу по пустякам.

Я уж боюсь представить как бы Вы реализовали selectList для выбора товара с ценой и остатком без использования логики в БД.

Из запроса не очень понятно, по каким полям связаны docs и остальные 2 сущности, текущий документ джойнится ко всем записям. Допустим, мы смотрим страницу документа и нам надо вывести выпадающий список материалов с ценой. Я бы сделал примерно так:

$term = 'Material';

$query = Materials::find();
$query->joinWith('curStocks');
$query->where(['warehouse_id' => $document->warehouse_id]);
$query->andWhere(['like', 'name', $term]);
$query->limit(50);
$materials = $query->all();

// foreach по результатам будет в обоих вариантах, независимо от местонахождения бизнес-логики
$data = [];
foreach ($materials as $material) {
    $price = getPrice($material, $document);
    $data[$material->id] = $material->name . ' - ' . $price;
}
renderSelectList($data);


Вы мне описали имплементацию обработки одной(!) строки позиции

Мне показалось, что слова «в один или несколько групповых инсертов» намекают на множественное число записей) Я привел пример для массовой обработки. Насколько он будет менее производительным, надо проверять на практике. У вас тоже на каждую строку будет дергаться триггер с несколькими селектами. Дело не в переключении контекстов, процессы обычно работают параллельно на разных ядрах, задержки в основном связаны не с процессором, а с вводом-выводом — сеть и диск. Кстати, база будет доступна для чтения/записи в вашем варианте, и можно ли это контролировать?
Вижу тут как минимум один лишний запрос из php к mysql серверу

Это будет один запрос на все время работы скрипта, а кроме того, его можно брать из кеша, а не из БД. То есть один запрос к БД на N пользовательских запросов на чтение.

Внешний php сервис писать который Питон будет дёргать?

Да, сервис называется API. Написание API не просто так стало популярным. Никакой кучи кода нет. Либо вы пишете код в БД, либо в PHP. Да, в клиентах API кода будет побольше, чем если бы они коннектились напрямую к базе, зато нет проблем с контролем доступа к таблицам, авторизацией, и прочими инфраструктурными вещами.

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

Я даже количество символов сравнил. В варианте на PHP кода меньше. То, что не создаются переменные с понятными именами, это скорее минус. Не совсем понял, что вы имели в виду насчет контекста, но то, что в рамках одного условия логические операторы служат разным целям, это тоже понятности не добавляет.

1. $oldAttributes = $this->oldAttributes;
Видимо придётся дёргать SELECT'ом — доп. нагрузка

Нет. При загрузке данных сущности из БД устанавливаются свойства в $this и те же самые в $this->oldAttributes. При обработке меняются данные в $this, а если надо, можно прочитать прежние значения из $this->oldAttributes.

У Вас будет генериться много запросов которые гоняются между php <=> mysql. Ещё нагрузка.

Селекты такие же, как и в вашем коде.

Как бы Вы реализовали, например, сервис копирования документов за период.

Не очень понятно, зачем копировать сущности, но это ладно. Скорее всего, я бы дернул их таким же селектом, обработал, и вставил новые данные в один или несколько групповых инсертов. Связанные сущности загружаются через дополнительный запрос с IN. В сложных случаях никто не мешает вызывать из кода запросы с INNER JOIN. То есть, счет запросов к базе идет на единицы или в крайнем случае на десятки, но никак не 100500. И мне кажется, это не такая частая операция, чтобы из-за нее переносить всю логику в базу.

Итоговый код триггера будет выглядеть так:
SELECT o.max_limit, o.name INTO max_limit, client_name FROM org o WHERE o.id = NEW.org_id_client;
IF NEW.sum > max_limit THEN
    CALL raise_error(CONCAT('Сумма ... не может превышать лимит ...'));
END IF;

В коде будет проще и понятнее:
if ($this->sum > $this->org->max_limit) {
    throw new MaxLimitException('Сумма ... не может превышать лимит ...');
}

Результат поиска org по id может быть закеширован и использован при обращении из любых других сущностей.

Или более красивый вариант с использованием функции
SET msg := (SELECT raise_error(CONCAT('Сумма ... не может превышать лимит ...'))
    FROM org o
    WHERE o.id = NEW.org_id_client
        AND NEW.sum > o.max_limit
);

А что тут красивого? Неочевидный код, напоминает какую-нибудь хитрую ассемблерную конструкцию. Одно сравнение относится к бизнес-логике, второе нужно для связи по ключу, а находятся они вместе в одном выражении WHERE. И «SET msg» в зависимости от них может и не случиться, хотя на первый взляд это просто присваивание.

Я создал триггеры, которые в каждом BEFORE триггере создают MEMORY TEMPORARY TABLE с одноимёнными столбцами

Аналог загрузки данных из БД в переменную. Вы придумали то, что при бизнес-логике в приложении появляется само собой.

Кода стало меньше, он весь в одном месте и он не дублируется! Такой код поддерживать очень легко.

В приложении код еще проще, и поддерживать легче.

Будет что-то типа этого
UPDATE doc_pos_tmp_trg
INNER JOIN docs ON doc_pos_tmp_trg.new_doc_id = docs.id
SET dp.new_price = get_price(dp.new_material_id, d.org_id_client)
WHERE dp.time = 'B' AND dp.type = 'I';

// function DocPos::beforeInsert()
$this->price = getPrice($this->material_id, $this->doc->org_id_client);


UPDATE docs
INNER JOIN doc_pos_tmp_trg ON docs.id IN (doc_pos_tmp_trg.new_doc_id, doc_pos_tmp_trg.old_doc_id)
SET sum = IFNULL(docs.sum, 0)
- CASE
    WHEN doc_pos_tmp_trg.old_doc_id = id
    THEN IFNULL(doc_pos_tmp_trg.old_kol * doc_pos_tmp_trg.old_price, 0)
    ELSE 0
  END
+ CASE
    WHEN doc_pos_tmp_trg.new_doc_id = id
    THEN IFNULL(doc_pos_tmp_trg.new_kol * doc_pos_tmp_trg.new_price, 0)
    ELSE 0
  END
WHERE doc_pos_tmp_trg.time = 'A';

// function DocPos::beforeInsert()

$oldAttributes = $this->oldAttributes;
$docId = ($this->doc_id ?: $oldAttributes['doc_id']);
$doc = Doc::find($docId);

if ($doc->sum === null) {
    $doc->sum = 0;
}

if ($oldAttributes['doc_id'] !== null) {
    $doc->sum -= $oldAttributes['kol'] * $oldAttributes['price'];
}

if ($this->doc_id !== null) {
    $doc->sum += $this->kol * $this->price;
}

$doc->save();

Сегодня эта ссылка указывает точно на метод под названием booleanConditional. А вот на что эта ссылка будет указывать спустя пол года — на это мы посмотрим спустя пол года

Поэтому надо указывать в URL не master, а хеш коммита. В результатах поиска github формирует ссылки с хешем.
https://github.com/systemjs/systemjs/blob/96fefe138ad4d60f46c3bdbb32a43490877209e7/lib/conditionals.js#L126-L147

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

А в чем именно проще? Вместо одного поля надо написать название функции в другом поле.

У ядра есть папка functions, в которой находятся ещё пять папок
arrays
dates
strings

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

Зачем вообще глобальные функции? С ними только лишние проблемы — с автозагрузкой, с засорением пространства имен. Все равно у связанных функций часто бывает одинаковое начало в названии — array_assoc_search, array_empty_keys.
Насчет 23
Можно в браузере открыть donkeyhot.org:70
Если именно $conn->query('SELECT * FROM docs'), то $sql = 'SELECT * FROM doc_pos', так как, судя по названию таблицы, doc_id != NULL.

Но вообще это делается как-то так:
$sql = 'SELECT * FROM doc_pos WHERE doc_id IN (:doc_id_array)';

Если записей совсем много, но надо все их вывести обработать, и именно без JOIN-ов, то можно разбить на блоки по N штук. Будет total/N запросов в базу, и логика в программном коде, с возможностью использовать OOP, VCS, и другие полезные аббревиатуры.

PS: В качестве примера:
https://habrahabr.ru/post/282844/#comment_8888240
Подскажите, а как в вашей модели объясняется преобразование «исходное изображение — контрастные переходы — колонки ориентации»? Насколько я представляю, это тоже должно быть основано на механизме самообучения клеток.
Интересовался этим вопросом одно время, даже в чем-то начал приходить к похожим выводам, но не было возможности всерьез этим заниматься, да и в математике я не настолько разбираюсь. Рад, что у вас что-то получилось.
Наверно, слово «развлечение» тут не очень подходит. Где-то на баше была цитата, что Хабр это как раньше журнал Юный Техник.
Если найти статью с Хабра в Гугле, то это источник полезной информации.
А если участвовать в холиварах, или писать статьи, это уже ближе к саморазвитию, или что-то вроде хобби. Например, получив минусы за технически неграмотный коммент, обычно задумываешься, что же я сказал не так, ищешь информацию, узнаешь что-то новое. Но опять же для кого как, кто-то вообще Хабр не читает.
Велись наверно, но проблема была явно в приложении, а сообщение приложения и так отображалось в интерфейсе. А локально все работало, так как иконка была в репозитории.
При открытии веб-приложения появлялось сообщение об ошибке БД, что id пользователя при вызове процедуры равно null. Сообщение было замечено пользователями при работе на тестовом сервере, потому что там все ошибки выводились через flash messages на фронтенд в виде всплывающих сообщений. Ошибка появлялась один раз на странице логина, при первом входе в систему, обычно с утра. Неавторизованного доступа в приложении нет.

Я нашел функцию, где происходит ошибка, функция вызывается из стандартного шаблона представления (layout), в котором по бокам разная информация для пользователя, в ней происходит вызов процедуры БД, куда передается user_id. Но он не может быть null, null он только на странице логина, а у нее свой шаблон.
В Firebug кроме GET-запроса страницы логина никаких других запросов к сайту нет.
Опытным путем выяснилось, что ошибка также появляется, если нажать Ctrl+F5 или Ctrl+Shift+R.

Причина
Открыл Chrome, и сразу нашел причину. На запрос favicon.ico выдается 404.
Firebug запрос фавиконки вообще не показывает.

При открытии сайта браузер запрашивает файл favicon.ico.
На тестовом сервере его почему-то нет, и на рабочем тоже.
Управление передается на index.php, приложение не находит подходящий роут и показывает ошибку 404.
Ошибка выводится со стандартным layout.
Текущего пользователя еще нет, база выдает ошибку, ошибка попадает в flash messages.
Открывается страница логина, показывает все flash messages из сессии.

От шаблонизатора в PHP только теги <?php ?> / <?= ?>, и конструкции include / require. Все, что внутри тегов — язык программирования.
Это никак не меняет наличие простой и банальной ошибки в вашем коде на продакшене. Которая к тому же светит файловую структуру сервера. Это хорошо показывает, к чему могут привести ваши советы.
Пожалуй, ссылка на эту ветку комментов тоже не помешает.
А что такое OneBox? Ни разу не слышал.

В качестве юмора (не переключил раскладку)

лучше если оно возможно без участия вас как «творца»

Складывается впечатление, что вы, видимо, получили на поддержку чей-то проект с кучей фабрик и инверсией зависимостей, ниасилили, и поэтому написали эту статью.
Нет. В начале разработки у вас есть выбор, в какую сторону пойти, налево или направо. Но если вы пойдете в неправильную сторону, вам потом придется поворачивать. То есть, исправлять и переделывать. Сумма двух сторон треугольника всегда больше длины одной стороны. Лучше заранее подумать и выбрать правильное направление, или хотя бы близкое к правильному. И принципы SOLID в этом помогают.
Ясно. А если надо 2 связанных параметра передать в сообщение и каждый выделить по-своему («Вы набрали 18 очков из 20 возможных»), у вас будет другая функция text2()?
В вашем примере только один тег <br/>, ни цвета ни болда там нет. У вас функция подстановки перевода сама магические спаны добавляет?
Ну так как вы будете делать это в вашем варианте с result?
Извините, я не очень понял, а в чем преимущество такого решения, в связи с вашими комментариями про теги? Как вы будете действовать, когда потребуется число сделать жирным шрифтом, а надпись «пользователей» — серым?

Information

Rating
1,967-th
Location
Россия
Registered
Activity