Профиль пользователя заблокирован сроком с 10 апреля 2022 г. и навсегда по причине: систематические нарушения правил сервиса
Ответы пользователя по тегу ООП
  • Как провести соответствие между строкой и классом с точки зрения SOLID?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Это всё очень плохо.
    В первом варианте человек должен знать, как мяукают кошки, а в последнем "правильном" варианте человек трогает не кошку, а кошачий голос(?!).
    В "идеальном" варианте опять же выбирается не животное, которое надо погладить, а его голос.

    Чтобы следовать принципам солид, надо понять в первую очередь ЗАЧЕМ это всё делается.
    А делается это для того чтобы уменьшить связность. Чтобы класс, использующий какой-либо функционал, не знал деталей его реализации. И, соответственно, мы могли бы менять реализацию без опасения поломать что-то в классе-пользователе.

    При этом extends, кроме как от абстрактного класса, эту связность всегда увеличивает.
    И его надо избегать. А использовать принцип Composition over inheritance. То есть нужный функционал получать не наследованием, а передачей независимых функциональных модулей в виде параметров.

    Соответственно, нам надо сделать иерархию: голос - животное - потрогать.
    И вот теперь у нас хоть голос, хоть животное, будут открыты для каких угодно изменений, до тех пор пока они поддерживают публичный контракт.
    / ******* голоса *******/
    abstract class VoiceEngine {
    	public function getVoice() {}
    }
    class CatVoiceEngine extends VoiceEngine {
    	public function getVoice() {
    		return "Meow!";
    	}
    }
    class DogVoiceEngine extends VoiceEngine {
    	public function getVoice() {
    		return "Bark!";
    	}
    }
    class HumanVoiceEngine extends VoiceEngine {
    	public function getVoice() {
    		return "Да пошёл ты!";
    	}
    }
    / ******* животные *******/
    abstract class Animal {
    	public function __construct(public VoiceEngine $voiceEngine) {}
    	public function say() {
    		echo $this->voiceEngine->getVoice();
    	}
    }
    class Cat extends Animal{}
    class Dog extends Animal{}
    class Human extends Animal{
    	public function touchAnimal(Animal $animal) {
    		$animal->say();
    	}
    }
    / ******* исполнение *******/
    $cat = new Cat(new CatVoiceEngine());
    $dog = new Dog(new DogVoiceEngine());
    $human = new Human(new HumanVoiceEngine());
    $human->touchAnimal($cat);
    $human->touchAnimal($dog);
    $human->touchAnimal($human);


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

    class AnimalFactory {
        public static function create($type) {
            return match($type) {
                'cat' => new Cat(new CatVoiceEngine()),
                'dog' => new Dog(new DogVoiceEngine()),
                'human' => new Human(new HumanVoiceEngine()),
            };
        }
    }
    $human = new Human(new HumanVoiceEngine());
    $human->touchAnimal(AnimalFactory::create('cat'));

    В итоге мы вернулись к тому же кейсу (match - это улучшенный case), но при этом у нас всё разделено, и каждый класс занимается строго своим делом.
    Ответ написан
  • Какой выбрать вариант избавления от глобальных переменных в ООП PHP приложении?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Определиться, что мы пишем - ООП или говнокод с глобалсами.
    Во втором случае совершенно без разницы, какие оттенки запаха будут у этого дерьма.

    В первом случае выкинуть весь этот ад и делать по-человечески, передавая переменные в конструктор/методы в качестве параметров.
    Ответ написан
    Комментировать
  • MVC (PHP): правильно ли понимаю слои?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Комментировать
  • Как спроектировать страницу авторизации с точки зрения паттерна MVC на PHP?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Или же я вообще пишу глупость

    В целом да. Но

    вся эта логика прекрасно будет в index.php лежать?

    - это гораздо большая глупость.
    Ну то есть лежать-то будет, но к MVC уже никакого отношения не будет иметь.

    По пунктам

    Юзер не должен принимать в конструкторе логин и пароль.
    Вот сейчас эта страница отображает мне двух юзеров помимо меня. Их обоих надо создавать с логином и паролем, серьёзно?

    Что такое AuthPage вообще непонятно. Модель, контроллер? По базе проверяет модель, куки пишет контроллер. А здесь какой-то кадавр.

    Перед тем как писать авторизацию "в стиле MVC", надо сначала разобраться, что такое модель, что такое контроллер, и что такое вью.

    Модель - это вся логика приложения.
    Контроллер - это интерфейс для общения модели с браузером. Делает всё, связанное с обработкой НТТР запросов.
    Вью - отображение.

    Как правильно.

    Соответственно в модели должен быть класс User с методом auth(), который принимает логин и пароль и возвращает инстанс класса Юзер.
    В конторе делается экшен: отдельный метод, который
    - проверяет, если был запрос методом ПОСТ, то берет из него логин и пароль,
    - валидирует их, если валидация не прошла, то создает ошибку, которую надо показать юзеру
    - если прошла, то вызывает метод auth() модели User, передавая в него логин и пароль
    - если совпали, то пишет в сессию ид юзера, и делает редирект куда-нибудь
    - если не совпали, то создает ошибку, которую надо показать юзеру
    - вызывает вью с формой для логина и пароля

    Для регистрации делается еще один экшен, который
    - проверяет, если был запрос методом ПОСТ, то берет из него данные для регистрации,
    - валидирует их, если валидация не прошла, то создает ошибку, которую надо показать юзеру
    - если прошла, то то заполняет класс User данными и выполняет метод save() и делает редирект куда-нибудь
    - вызывает вью с формой для регистрации

    Для личного кабинета делается третий экшен, который берет из сессии ид юзера, обращается к методу read() модели User и через View показывает личный кабинет

    Варианты реализации

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

    То есть папка user в которой есть, скажем, файл index.php который является экшеном личного кабинета.
    Он проверяет юзера в сессии, и если нету, то перекидывает на auth.php
    в auth.php есть форма и ссылочка на register.php
    Все три файла инклюдят в себя файл user.php из папки model, в котором есть функции auth(), register() и profile()

    Но в более классическом варианта к трем буквам MVC добавляется ещё одна - R, роутер. Специальный сервис, который разбирает адресную строку, и видя, например, что к сайту обратились по адресу /user/register, создаёт экземпляр класса UserController и вызывает его метод register()
    Ответ написан
    4 комментария
  • Можно ли в PHP автоматически вызвать класс?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Во-первых, это называется не процедурное, а функциональное программирование
    Во-вторых, я снова очень сильно сомневаюсь, что это именно то что тебе нужно. В частности, "вызов класса" - это бессмыслица, вызываются методы, а не сами классы
    В-третьих, по аналогии с функциональным программированием, это называется анонимный класс

    (new class {
        public function log($msg)
        {
            echo $msg;
        }
    })->log("hello");
    Ответ написан
    7 комментариев
  • Реализую method chaining. Как сделать так, чтобы вызов метода влиял на общий результат?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Учись задавать вопросы. То что ты сейчас понаписал - это называется проблема XY, бич всех новичков. (ну и конечно бич всех помогаек на тостере, которым некогда думать, им надо на вопрос отвечать).

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

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

    class foo {
        protected $bar;
        public function bar(string $data ) {
            $this->bar = $data; 
        }
    
        private function baz() {
            if (isset($this->bar)) {
                // сделать что-то
            }
        }
    }
    Ответ написан
    1 комментарий
  • Как избежать повтора инклюдов в ООП PHP?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Из всей той ерунды, которую тут уже успели понаписать, и ещё понапишут самозваные "кураторы" и эксперты, единственно полезным является ответ Андрей Ежгуров
    Но он отвечает на следующий твой вопрос.

    А ответом на текущий является

    Передавать его в класс параметром

    Это единственно правильная практика, пусть она даже и кажется тебе не очень хорошим решением.
    Только не целиком конфиг а лишь те опции, которые нужны данному классу.
    Это то как на самом деле работает ООП. Для закрепления материала можешь почитать про dependency injection.
    Ответ написан
    6 комментариев
  • Доступ к измененному свойству родительского класса?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    vrrcvqiy17x51.png
    Как раз картинка в тему. Ну почти.

    $b - это совершенно отдельный объект, который ничего не знает, что происходит в $a
    Ответ написан
    Комментировать
  • Возможно ли корректировать работу метода в зависимости от объекта?

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

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Вопрос хороший и правильный. Всё остальное - не очень.
    К сожалению, то что у тебя сейчас - это тоже говнокод и неправильно.

    Тут надо понимать одну очень простую, но очень неприятную вещь: ООП, в отличие от ПХП - это сложная тема. Явочным порядком, там подглядев, тут скопипастив - ООП изучить нельзя.
    Максимум что у тебя получится - это та же процедурщина, вид сбоку. Неподдерживаемый говнокод, просто по-обезьяньи обернутый в классы.

    Но учиться все равно надо.
    Главное что надо понимать про ООП - оно не про сами классы, а про их взаимодействие.
    именно поэтому то самое жлобал и является говнокодом. Потому что никакого взаимодействия нет ,и класса тоже нет - есть функция, которую если вынуть из класса, то НИЧЕГО не изменится.

    Но и то что у тебя сейчас - это не ООП.
    Каждый раз создавать новое подключение можно и без всякого ООП. Вот только оно убьёт тебе сервер БД.
    Чтобы было ООП, надо передать уже созданный инстанс класса для работы с БД как параметр конструктора.

    Кроме того, класс qpdo - это какой-то анекдот, бессмыслица. Обезьяна увидела как человек носит очки, напялила на нос ложку и ходит с гордым видом. Внешне вроде то же самое, но смысла никакого.
    Ты можешь объяснить, ЗАЧЕМ тебе класс qpdo? Чтобы настройки прописать? А ты подумал что настройки бывают РАЗНЫЕ? Что дома у тебя логин рут и пароль пустота, а на хостинге это не прокатит. И что - будешь код переписывать, каждый раз заливая из дома на сервер? Серьёзно?
    Настройки должны всегда лежать отдельно. А больше ни для чего твой класс не нужен.

    Поэтому,

    1. qpdo выкинуть на помойку, по крайней мере до тех пор, пока не поймешь, для чего тебе свой класс, и как с ним обращаться.
    2. Создать один раз инстанс класса для работы с БД (в простейшем случае - PDO) и передавать в другие классы через констркутор
    3. в конструкторе присваивать его переменной класса, которую использовать для доступа к БД.

    В итоге возвращаемся к исходному вопросу, как передать соединение в другой класс:
    class somethingClass {
      function __construct($db) {
        $this->db = $db;
      }
    
      function somethingFunction(string $key) {
        $query = $this->db->prepare("SELECT `key` FROM `table` WHERE `key` = :key");
        $query->execute(array(':key' => $key));
        return $query->fetchColumn();
      }
    }


    Бонус
    echo 'Ошибка подключения к БД MySQL: ' . $error->getMessage() . ''; die;
    - это ад и говнокод.
    Да, в прошлом веке так писали. С тех про прошло уже 20 чертовых лет. 20, Карл! Тебя небось еще в проекте не было.
    Сайт, который вываливает вот это всё на всеобщее обозрение - это позор.
    Все ошибки сайт должен всегда держать при себе. И никому не показывать. И поэтому никогда не надо лезть руками в ту ошибку, которую выбрасывает РНР. Надо дать ей спокойно пойти туда, куда идут все остальные ошибки.
    Ответ написан
    Комментировать
  • Знать ООП или нет?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Смотря что понимать под этим словом.
    Программисту - надо
    Говнокодерам, вайтишникам, одинэсникам, вордпрессникам, эникейщикам и прочим тыжпрограммистам - не нужно.
    Ответ написан
    Комментировать
  • Объявить свойство как объект - ошибка?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    То есть ты искренне считаешь, что если в каком-то месте кода напишешь
    $this->shop = new Shop;
    эта переменная сразу получит доступ ко всем объектам класса Shop, когда-либо созданным в коде?
    И твой пример можно упросить до такого

    class Person {
            public function __construct(){
                $this->name = '';
            }
     
            public function showname(){
                echo $this->name;
            }
    }
    $name = "Вася";
    $person = new Person;
    $person->showname();

    и потом удивляться, почему он ничего не выводит?
    Ответ написан
  • Как описать структуру PHP объекта?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Классы могут выглядеть как угодно.
    К синтакису вызова "структура объектов" (на самом деле классов) не имеет никакого отношения.

    В РНР используется унифицированный синтаксис. К результату любого выражения в РНР можно обратиться так же как к переменной.
    Если выражение возвращает массив, то можно приписать скобочки и обратиться к элементу возвращаемого массива
    echo function()[1];
    Если выражение возвращает объект, то можно приписать стрелочку обратиться к методу или свойству
    echo function()[1]->hello;

    Следовательно, чтобы писать стрелочку после метода, метод - сюрприз - должен возвращать объект. Себя или любой другой - без разницы
    Ответ написан
    Комментировать
  • Где тут ошибка?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    То есть ты пишешь класс еггог, но при этом не умеешь включать элементарное отображение ошибок в РНР?

    Забудь про классы на годик или два, сотри этот класс и учи основы. Это не наезд - я серьёзно.
    Всё, что новички так старательно выписывают в своем коде, на самом деле уже есть РНР. Ничего от себя писать не надо. Особенно класс еггог.

    Всегда пиши в начале своего кода две строчки
    error_reporting(E_ALL);
    ini_set('display_errors', 1);

    на боевом сервере меняй 1 на 0.
    Это ВСЁ что нужно для обработки ошибок
    Ответ написан
    3 комментария
  • Как передать параметры из одного свойства-объекта в другое свойство-объект?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Не очень понятно, чем это отличается от ситуации, когда объекты не являются свойствами одного класса. На мой взгляд - ничем не отличается. Так что да - передвать все объекты "вручную". Старый добрый dependency injection

    А если несклько таких свойств-объектов, то, возможно, пересмотреть структуру классов или то как они создаются. Задействовать фабрику или контейнер
    Ответ написан
  • ООП Пользователи?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Нужно забыть про ООП на пару годков минимум, и заняться изучением базовых элементов языка - циклы, условные переходы, отделение логики приложения от логики отображения
    Ответ написан
    2 комментария
  • Как задать значение по умолчанию для статической переменной?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Вместо переменной использовать метод.

    А вообще если такие вопросы возникают, то скорее всего ООП применяется не по назначению, и внутри голимая процедурщина, глазированная красивыми словами "паблик" и "статик". И лучшим решением будет не пытаться изображать из себя программиста, а писать по-старинке, функциями внутри которых использовать global. принципиально для этого кода разницы не будет.
    Ответ написан
    Комментировать
  • Зачем передавать экземпляр класса в качестве аргумента?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Забудь на время про ООП. Учи базовый синтаксис.
    Разберись с тем, для чего вообще нужны параметры.

    Посде этого таких вопросов возникать не будет.
    Ответ написан
    5 комментариев
  • Нужен ли объект в данном примере?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    ну вот конкретный пример, как сохранить объект в БД? Таким образом как я сделал?

    Если ты создал класс для того чтобы сохранять его объекты в БД - то нет, не нужен.

    Вообще, порог вхождения в ООП очень высокий. Особенно в сравнении с порогом вхождения похапе. Из 100 человек, пользующихся РНР, хорошо если один может понять, зачем ему классы. Так что в целом ничего страшного, если ты не понимаешь. Хотя мне кажется, что ты движешься в правильном направлении. Просто не надо думать, что с ООП получится так же легко и само собой, как с процедурщиной. Тут надо мозги нанизнанку выворачивать.

    А с классом надо так - сначала думаешь,зачем он тебе, а потом создаешь. Не наоборот :)
    Ответ написан
    2 комментария
  • Как совместить реляционную модель бд с ООП?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Неужели сама идея реляционных бд с ООП несовместима?


    Ты весьма недалёк от истины.
    Некоторые сравнивают эту проблему с проигранной США войной во вьетнаме.

    Решение от Victhor подходит для единичных выборок, но как быть с коллекциями? Люди начинают изобретать разные стратегии подгрузки - lazy loading, eager loading. Вот Елоквент, например, собирает в кубышку все айди загруженных товаров и потом пуляет 1 запрос с IN (...) чтобы получить для них бренды, которые потом в цикле пришпандоривает к товарам.

    Но все это очень быстро начинает сказываться на производительности. И всё дальше затягивает нас в пучину Вьетнамской войны, или другими словами в кроличью нору Object-relational impedance mismatch - как по-научному называется озвученная тобой проблема. Дальше начинается совсем уж треш и угар, типа дико тормозащих коллекций в Доктрине или таких извращений, которыми занимался один мой знакомый - он плодил вьюхи в БД, поскольку его ОРМ умел работать только с одной таблицей.

    Или все же модифицируются в дальнейшем классы, переписыванием кода?


    В одном ты можешь быть уверен: такое легкое отображение, которое рисует тебе воображение при первом знакомстве с ORM - чтобы так хоба-хоба, и у нас записи из БД отобразились в объекты - увы, не существует. Да, приется переписывать, и много. И чем сложнее взаимосвязи, тем хуже будет работать автоматизация.

    И тут на первый план выходит организация ORM и становится очевидной превосходство стратегии Data Mapper, когда слой работы с БД полностью отвязывается от объекта бизнес-логики.

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

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

    И это мы сейчас говорим о read-моделях, и даже не трогали тему write models - то есть сохранения измененного объекта - со всеми гроздьями смежных объектов(!) - в БД!
    Ответ написан
    4 комментария