Pull to refresh
13
0
Алексей @boodda

Пользователь

Send message
zuse Но где же само задание ?)
Анкету я заполнял, оповещение об оставшихся двух днях тоже получил. Наверно я не справлюсь)))
Например, разного рода интеграции одних сервисов в другие. Бывают сервисы с огромным API. Написание кода по встраиванию могут сопровождаться разными проблемами, в том числе концептуальными. И никто не может внятно оценить время на это, от дня до года. Одну из систем интегрировали почти год). В момент планирования, у скрам-мастера текут кровавые слезы, а у менеджера проекта появляются тики.
«Мы хорошо знаем о существовании гендерного разрыва в области %s» так начинается любая тема которую хотят натянуть на глобус. Мне кажется, где-то есть методичка о том как писать подобные статьи. Постулировать проблему которой нет и размышлять над ущербным решением. Поясните, почему это является проблемой? для кого это проблема? для женщин? оно им в надо? кого-то разве притесняют? неравенство возникло по чьей-то прихоти? отказали в праве на учебу и работу в it? так много вопросов к первой же строке, а ответов на них и не дано. это просто унылость

Прикольный слон. Кстати сделан скорее всего с помощью https://github.com/fogleman/primitive.


Вот бота писал для телеграмм


Заголовок спойлера
const fs = require('fs');
const exec = require('child_process').execSync;
const temp = require("temp").track();
const pathComponent = require('path');
const TelegramBot = require('node-telegram-bot-api');

const Settings = function () {
    this.defaultSettings = {
        rect: 1,
        type: 0
    };

    this.container = {};
};

Settings.prototype = {
    getValue: function (chatId, value = null) {
        if (this.container.hasOwnProperty(chatId)) {
            return value ? this.container[chatId][value] : this.container[chatId]
        }

        return value ? this.getDefault()[value] : this.getDefault();
    },
    setValue: function (chatId, key, value) {
        if (!this.container.hasOwnProperty(chatId)) {
            this.container[chatId] = this.getDefault();
        }

        if (Object.keys(this.defaultSettings).indexOf(key) === -1) {
            console.log('Try to set unknown setting: ' + key);
            return;
        }

        if (key === 'type' || key === 'rect') {
            value = parseInt(value, 10)
        }

        this.container[chatId][key] = value;
    },
    getDefault: function () {
        return Object.assign({}, this.defaultSettings);
    }
};

const settingsContainer = new Settings();

// Устанавливаем токен, который выдавал нам бот.
const token = '';
const downloadDir = './downloaded/';

const rectOptions = {
    reply_markup: JSON.stringify({
        inline_keyboard: [
            [{text: 'Треугольники', callback_data: 'rect_1'}],
            [{text: 'Прямоугольники', callback_data: 'rect_2'}],
            [{text: 'Эллипсы', callback_data: 'rect_3'}],
            [{text: 'Круги', callback_data: 'rect_4'}],
            [{text: 'Прямоугольники(rotated)', callback_data: 'rect_5'}],
            [{text: 'Кривые', callback_data: 'rect_6'}],
            [{text: 'Эллипсы(rotated)', callback_data: 'rect_7'}],
            [{text: 'Полигоны', callback_data: 'rect_8'}],
        ]
    })
};

const typeOptions = {
    reply_markup: JSON.stringify({
        inline_keyboard: [
            [{text: 'Простая картинка', callback_data: 'type_0'}],
            [{text: 'Движущаяся картинка', callback_data: 'type_1'}]
        ]
    })
};

// Включить опрос сервера
const bot = new TelegramBot(token, {polling: true});

bot.onText(/\/start/, function (msg) {
    bot.sendMessage(msg.chat.id, '/status /settype /setfig');
});

bot.onText(/\/status/, function (msg) {
    bot.sendMessage(msg.chat.id, 'I am alive!');
});

bot.onText(/\/setfig/, function (msg) {
    bot.sendMessage(msg.chat.id, 'Выберите фигуры из которых будет формируется изображение:', rectOptions);
});

bot.onText(/\/settype/, function (msg) {
    bot.sendMessage(msg.chat.id, 'Выберите тип возвращаемого изображения:', typeOptions);
});

bot.on('callback_query', function (msg) {
    let chatId = msg.message.chat.id;

    let answer = msg.data.split('_');
    if (answer.length === 2) {
        settingsContainer.setValue(chatId, answer[0], answer[1]);
    }
    bot.sendMessage(chatId, 'ok');
});

bot.on('message', function (msg) {
   console.log(msg); 
});

bot.on('photo', onPhoto);

async function onPhoto (msg) {
    let chatId = msg.chat.id;

    try {
        let image = initImage(msg);
        let originalImagePath = await bot.downloadFile(image.file_id, downloadDir);
        let ext = pathComponent.extname(originalImagePath);
        let resizedImagePath = temp.path({suffix: ext});
        let processedImagePath;

        await resizeImage(image, originalImagePath, resizedImagePath);

        let settings = settingsContainer.getValue(chatId);
        console.log(settings);
        if (settings.type === 0) {
            processedImagePath = temp.path({suffix: ext});
            makeSimpleImage(settings, resizedImagePath, processedImagePath);
            await bot.sendPhoto(chatId, processedImagePath, {});
        } else if (settings.type === 1) {
            processedImagePath = temp.path({suffix: '.gif'});
            await makeGif(settings, image, resizedImagePath, processedImagePath);
            await bot.sendVideo(chatId, processedImagePath, {});
        }

        temp.cleanupSync();
    } catch (err) {
        console.log(err);
        return bot.sendMessage(chatId, err);
    }
}

function initImage(msg) {
    let photo = msg.photo || [];
    let lastFileSize = 0;
    let maxResId = -1;
    let i;

    for (i = 0; i < photo.length; i++) {
        if ((photo[i].file_size || 0) > lastFileSize) {
            maxResId = i;
        }
    }

    if (maxResId === -1) {
        throw new Error('No photo info');
    }

    let msgPhoto = photo[maxResId];
    ['file_id', 'width', 'height'].forEach(function (prop) {
        if (!msgPhoto.hasOwnProperty(prop)) {
            throw new Error('No photo size');
        }
    });

    return msgPhoto;
}

function resizeImage (image, from_path, to_path) {
    let resize = image.width > image.height ? '256x' : 'x256';
    return exec('convert -geometry ' + resize + ' ' + from_path + ' ' + to_path)
}

function makeSimpleImage(settings, from_path, to_path) {
    return exec('primitive -n 250 -m ' + settings.rect + ' -i ' + from_path + ' -o ' + to_path)
}

async function makeGif(settings, image, from_path, to_path) {
    let dir = './series/' + makeRandName();
    let size = image.width > image.height ? '512:' : ':512';

    if (!fs.existsSync(dir)){
        fs.mkdirSync(dir);
    }

    for (let i = 1; i <=5 ; i++) {
        makeSimpleImage(settings, from_path, '/ ' + i + '.svg');
        exec('node node_modules/svgexport/bin/index.js ' + dir + '/' + i + '.svg ' + dir + '/' + i +'.png ' + size);
    }

    let makeGifCommand = 'convert -loop 0 -delay 12 ' +
        dir + '/1.png ' +
        dir + '/2.png ' +
        dir + '/3.png ' +
        dir + '/4.png ' +
        dir + '/5.png ' +
        to_path;

    console.log(makeGifCommand);

    return exec(makeGifCommand);
}

function makeRandName() {
    let text = "";
    let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for (let i = 0; i < 8; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}
боль вы мою почувствовали, и её я не скрывал, но на ответы мои вы так и не ответили. Но я буду надеяться и ждать
«мне сказали делать — я делаю», это результат того, что «мы все решили — ваше дело сделать, кто не согласен может уходить»
Скажите честно, это были вполне коммуникабельные профессионалы своего дела? могли они также эффективно работать без скрама? Дал ли скрам стабильный прирост эффективности хотя бы 20% на протяжении года или двух? Было ли это экономически оправдано? То есть, дополнительное время которое уходит на планирование в рамках команды, ретроспективы, митинги, демонстрации помноженное на стоимость человеко-часа, оно приносит экономический эффект для этих продуктов? ну и про реалии тоже не стоит забывать в вашем ответе
По скраму команда самоогранизуемая, и вот когда стекхолдеры спросят с PO за просраный дедлайн, тот в лучших традициях, скажет — «команда сработала не так, а я что?? Я то все по скраму делал, а вот Вася с Петей в скрам не верят вот и все пошло по п… не так».
Вот как так получилось? Вроде на русском написал, но как то не очень похоже на русский.
Вы похожи на Свидетеля Скрама. Вам показалось, что вы поняли все, но на самом деле, вам кто-то продал мысль о том, что некие модели управления могут заменить лидера. И вот такие недолидеры, которые боялись брать на себя ответственность, потому что они хреновые лидеры и все просирали, придумали как её(ответственность) размазать по куче людей. И в случае чего сказать — это не я говно, а команда. Я то все по скраму делал…
Извините, но сначала вы говорили про календарный месяц(последний день месяца), а потом, про шаг от 28 до 28 дня. Это несколько разные задачи. Тогда решаемая задача не до конца очевидна.
У вас там же таймштамп, добавляете к пришедшей дате день, обнуляете время, прибавляете месяц и отнимаете секунду. это должно быть универсальным решением
Разговор идёт о всего-то 50к документов, а у вас навёрнуто столько… Толи и у вас индексов нет, толи вместо сервера raspberry pi. Тот же postgre, при самой простой настройке и грамотных запросах выдавал бы результаты за сотню миллисекунд, со встроенным полнотекстовым поиском. а написание обвязки на php без ларавела(можно было бы люмен взять или на симфони сделать микросервис) сэкономило бы ещё 150 миллисекунд. Короче, странно все это
Вынос бизнес логики из кода под фреймворком, несет не тот смысл, смысл не в том чтобы сменить фреймворк. Смысл в том чтобы доменный слой(бизнес-логика) была отделена от инфраструктуры(фреймворк), это позволяет сделать логику прозрачной для следующих программистов которые будут сопровождать и развивать вашу систему. А не так что, ну мы тут сохранили модель, в мидлвере изменили модель, обсервер выбросил событие в rabbitmq и с другой стороны получили это событие и что то изменили в БД. Все это будет почти невозможно понять тем кто придёт обслуживать вашу систему послезавтра, когда вы и ваши текущие коллеги уволитесь.

Проблема Laravel в том, что если делать так как предлагаете вы, т.е. не использовать паттернов и просто писать код, то вскоре вы с удивлением обнаружите, что не вы диктуете фреймворку как вы хотите чтобы это работало, а он вам.


Мое мнение бизнес-логика отдельно, инфраструктурный код отдельно, написано на фреймворке, и там никакой логики, только адаптеры между кодом бизнеса и фреймворка.
Но этот подход даёт профит когда у вас большой проект.


Большой проект в моем понимании — это когда пару сотен контроллеров, 200+ таблиц со сложными моделями данных, админка с аналитикой и подобные веши, версионированное API, фоновые задачи, интеграция с внешними источниками или хранилищами данных. Все это должно быть покрыто юнит и интеграционными тестами. Вот тогда вся эта магия ларавела, начинает выходить боком.


Посмотрите код ваших приложений, и задайте следующие вопросы:


  • Есть ли такое, что Eloquent модели попадют в представление ?
  • Используются ли в представлении Eloquent связанные отношения, например:
    @foreach($user->somerelations as $relation)
    {{ $relation->someattr }}
    @endforeach
  • Путешествуют ли ваши сущности внутри других сущностей ?
  • Есть ли у такие сущности, которые изменяются(вызывается сохранение в БД) в разных местах кода?
  • Используете ли вы Observers для изменения данных сущности перед сохранением в БД ?

Если вы ответили да, то эти решения убьют ваш проект, если он захочет расти. И это только, первое что пришло на ум.

Опять. Снова пропустил тег перевод.

1 и часть второго: Я несколько раз вкорячивался с разными библиотеками, и фреймворками. С laravel дваджы, когда менялось куча всего, и надо было много править. Теперь я не пишу бизнес-логику с внутри проекта на фреймворке. Бизнес-логика — отдельно, фреймворк как инфраструктура — отдельно.

2. В долгую, копипаста лучше, чем 1 абстрактный и потомки с перегрузкой методов. Про трейты п.7.

3. Eloquent закрывает только микропроекты. Так как это active record, просто он по сути предназначен для CRUD, тут да это прям инь и янь, но все остальное — это не очень удачные попытки заменить SQL синтаксис, не удобным(когда начинают монструозные запросы на нём писать) и бажным(issues по eloquent открыты всегда десятками) PHP кодом. И Doctrine конечно вроде мощнее, но та же проблема. Многословно, новый синтаксис.

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

5. $this->model это что, это уже какой-то объект, кто его туда положил, почему он там, в каком он состоянии? Вы не можете сказать. Но ожидаете что он «чистый», чтобы породить новый объект. Вызов же статичного метода, гарантировано вернёт вам новый объект с установленными значениями. Короче это метод класса, не метод объекта. Ну по-крайней мере обычно все это предполагают.

6. Я думаю, что если у вас по коду возникает проблема N+1, значит походу, пора отложить Eloquent(Он про CRUD) и перейти на SQL. Возможно DataMapper вас спасет. А может и нет.

7. Сортировка — это свойство запроса, а не класса. Ну вот просто, что такое сортировка? может она быть у репозитория? Вот мне кажется нет, но это только моё мнение. Та же история с отношениями.
Удалено. Не в ту ветку.

У Вас тут явно масса проблем. Этот подход не применим в приложениях хотя бы мелкого размера. Возможно только в совсем крохотных, где весь код можно уместить в голове.


По пунктам:


  • Почему ваш код находится в пространстве App\Http\Controllers ?


  • Если речь идёт про DDD. То надо было реализовать всю логику приложения в отдельном пространстве. Вообще не в папке с Laravel. Где то в /src/. Вот туда надо было складывать ваши абстрактные репозитории. А на Laravel лишь реализовывать адаптеры, по интерфейсам. А вообще, делать универсальный репозиторий — идея так себе. На практике это не работает. Хоть они и похожи, всегда есть отличия. Тут надо событие бросить, тут что то не сохранять. В итоге каждый репозиторий будет иметь методы, которые будут перегружать методы родительского класса. Лучше делать отдельный, несвязанный реп, к каждой из сущностей. Так будет проще впоследствии.


  • У вас репозиторий сетит в себя модель при инстанцировании, что потом приведет к проблемам. Что делать если вы захотите создать сразу 3 объекта Pizza?

В create должна быть фабрика.


$class = get_class($this->model); 
$newObj = new $class();
$newObj->fill($data);
return $newObj;

  • Сортировка как свойство класса? Really. Ну на вкус и цвет конечно, но видать вы еще не усвоили, что использование явных параметров в функциях лучше, чем свойства объектов. Все потому что у вас в классе 6 методов, а сортировку используют лишь 2. Подумайте об этом.


  • $this->model->hydrate, опять же, используйте статичные фабричные методы. {Model}::hydrate() или по аналогии с п.3


  • Ну и ответ на вопрос: — как справиться с жадной загрузкой?
    Ответ: Не использовать. Да как бы привлекательно это ни было.
    На край — не должна покидать пределы репы.


  • Трейты с сортировкой в мусорку.
Вам). Но если вам не надо, то может еще кому пригодится, мне бы вот пригодилось.
В PHPSTORM при настройке надо будет ввести вместо bridge название сети для контейнера с PHP.
Посмотреть можно командой docker network ls

Где же была эта статья неделю назад… Пришлось угробить 2 дня и с 3-го подхода проекты были подняты и под windows. От себя добавлю, если у вас все работает под linux, но выдаёт рандомные ошибки, то выполните(я все выполнял в PowerShell) для всех dockerfile и всех sh файлов, которые участвуют в сборке образов


dos2unix Диск:/путь/до_файла

А так же надо помнить о разнице в сетевом окружении между Linux и Windows. В Linux внутренняя сеть докера видна снаружи системы, а в Windows она за NAT'ом(Docker vEthernet) и не видна снаружи, и это приводит к проблеме с Xdebug, так как remote_connect_back=1 не работает в принципе. Если кто то знает как пробросить трафик из внутренней сети во внешнюю, напишите.


Для нашей компании мы выбрали следующее решение:


  • Создал папку mods_available
  • В Dockerfile добавил строку
    ADD ./mods_available/*.ini /etc/php/7.0/mods_available/
  • в корне проекта добавили в .gitignore запись игнорить все файлы ini в mods_available
  • В папку положил .gitkeep и xdebug.ini.dist

Содержимое xdebug.ini.dist


zend_extension=xdebug.so # или как там, не помню на память 
xdebug.remote_autostart = 0
xdebug.remote_enable = 1
xdebug.remote_host = 10.0.75.1 # Сюда написать IP хоста, из виртуального адаптера docker'а 
xdebug.remote_mode = "req"
xdebug.remote_port = 9000
xdebug.idekey = "PHPSTORM"

Если надо использовать xdebug, надо просто скопировать и переименовать xdebug.ini.dist в xdebug.ini и прописать в него свой IP. Это будет работать и в Windows и Linux. И даёт возможность не вносить изменения в файлы под git'ом.


И да все это работает на windows 10 под Hyper-v, без VirtualBox и др.

1
23 ...

Information

Rating
Does not participate
Location
Россия
Registered
Activity