В общем - пишу импортёр данных для интернет магазина из web api стороннего сервиса(что-то похожее на 1С). И столкнулся с проблемой, что класс импорта уже превышает 2500 строк, который состоит из однотипных методов типа importProduct, который берет данные из апи и при необходимости обрабатывает; и методов типа addProduct, которые принимают данные из предыдущего метода и пишут их в бд через AR(yii2). В итоге получается что все эти методы делают одно и тоже, но свести все к одной реализации все равно не получается из-за какой-никакой, но разнице в моделях. Получается, что весь мой код в функциональном стиле, а хотелось бы в объектном, но уже которую неделю ломаю голову, как это сделать так, чтобы было и красиво, и читабельно и хоть немного бы следовало принципам SOLID. А в данный момент на лицо принципы STUPID:)
Хотел бы попросить у опытных людей совета, как бы это сделать лучше, и имеет ли это вообще смысл, т.к. в принципе это все работает, но если где-то что-то поменяется, то переделывать придётся чуть ли не каждый метод.
Есть отдельный класс, который отвечает за получение токена и получение JSON из апи. Он, собственно, идет зависимостью в класс импорта. Сами методы примерно такие:
public function addProduct($data)
{
foreach ($data as $value) {
// тут логика везде одна и та же:
// проверяем есть ли сущность с таким GUID уже в БД
// если нет - добавляем
// если есть связи - добавляем
// если есть - проверка для каждой сущности своя и, при необходимости, обновление
}
}
public function importProduct($guid = null)
{
// тут либо дергаем все, что дает апи
$this->connect->query("тут конкретный запрос к апи");
// либо, к примеру, с продуктами - проходимся циклом по категориям
// или же, если нужна конкретная запись, то извне можно указать аргумент $guid
// и в цикле уже вызываем метод addProduct($data)
}
// еще есть методы типа getSomething()
// но они только для специфичных моделей
// типа файлов и т.п.
В слепую, конечно, сложно судить, но исходя из общих практик запись можно в отдельный уровень вынести - слой репозитория. Однотипные функции обработки попробовать оформить паттерном «стратегия», с соответствующими инъекциями контрактов реализаций.
По описанию - подходящий случай для Dependency Injection
function doSameAction(AbstractDirrefentClass $di)
{
// делаем общий код
$di->makeDifference($data); //А весь различающийся реализован в классах, реализующих один и тот же интерфейс
}
Как вариант, этот $di можно передавать не в каждую функцию, а в конструктор класса и присваивать приватному члену.
montray, смысл паттерна в следующем.
У вас есть некие данные, обладающие собственной внутренней логикой.
У вас есть некие операции, которые проводятся над всеми такими данными одинаково, различия только в их внутренней логике.
Паттерн позволяет разнести эти две логики (обычно еще и лежащие на разных уровнях абстракции) и сделать логику операций независимой от логики данных.
Потом введение данных нового типа потребует только описать их логику.
(Описание паттерна намеренно упрощено и приближено к заявленному случаю).
Я думаю тут стоит руководствоваться таким принципом: "Работает, не трогай".
Иногда мне кажется лучше все с нуля переписать, чем пробовать исправить это.
К тому же тут я как понимаю проблема не в скорости работы, всего лишь в том что не нравиться как выглядит? Тогда точно стоит переписать все с нуля если это принципиально. А там уже решаешь сам.
Это сугубо мое мнение.
Да, конечно, это все нужно переписать. Вопрос как раз стоит в том - как? Возможно, я плохо объясняю, но в двух словах - все эти методы для каждой сущности имеют одну и ту же логику: импорт просто берет данные из апи, add методы проверяют на наличие, добавляют запись либо же обновляют, добавляя необходимые связи. Пробовал привести все к одной абстракции, но по итогу, из-за малейшей разницы методы в дочерних классах чуть ли не каждой сущности приходилось переопределять. Возможно, конечно это и есть лучшее решение, но я не особо силён в паттернах, и может быть кто-то все таки подскажет:)