Задать вопрос
Профиль пользователя заблокирован сроком с 10 апреля 2022 г. и навсегда по причине: систематические нарушения правил сервиса
  • Реализую 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 комментарий
  • Как перейти по ссылке передавая запрос POST через PHP?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Никак

    РНР не ходит по ссылкам. По ним ходит браузер.
    Попробуй научиться отличать одно от другого.
    Ответ написан
    Комментировать
  • Как правильно писать catch в php?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Для того чтобы правильно писать catch, надо писать осмысленный код.

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

    Но самое забавное, что при всём при этом контроллер всё равно пытается выполнять работу модели. Казалось бы, какое отношение интерфейс для обслуживания НТТР запросов имеет к транзакциям в базе данных? А вот поди ж ты!

    Чтобы сделать этот код осмысленным, контроллеру всё-таки придется потрудиться, и выполнить какую-то работу самому, а не перекладывать на "сервис". А так же отдать модели то что ей принадлежит.

    В общем транзакцию перекинуть в createDefault. причём не напрямую, а ещё ниже - в слой для работы с БД. Стартовать транзакцию до валидации данных - это как бы *не совсем логично*, мягко говоря. И в итоге, как по волшебству, весь этот говнокод исчезнет как страшный сон.
    При этом в параметрах передавать не НТТР реквест чохом, а осмысленный набор параметров, вынутый предварительно из реквеста!
    В частности, если модель сама проверяет права доступа, то и передавать ид пользователя из авторизации.

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

    В любом случае, уж catch (Exception $e)-у тут точно не место

    Но это если рассматривать твой конкретный случай.
    В общем же случае правильный код написал Илья.

    То есть внутри трая операции с БД и коммит.
    в кетче роллбэк и перевыброс исключения. Только ловить надо Throwable

    try {
      DB::beginTransaction();
      // запись в БД
      // запись в БД
      // запись в БД
      DB::commit();
    } catch (\Throwable $e) {
      DB::rollBack();
      throw $e;
    }
    Ответ написан
    2 комментария
  • Правильное использование связки Symfony+Git+Composer+Docker?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Докер выполняет ту роль, которую до этого у тебя играли всякие денверы, опенсерверы, и прочая вампа-юмба. Вместе с панельками на хостинге.
    Кроме симфони есть еще много других программ. Которые устанавливаются через композер. Надо уметь добавлять пакеты в проект, как самому, так и добавленные другими. Надо уметь разворачивать локально проект, который использует композер. Надо уметь читать composer.json. А по-хорошему ещё и писать. Но вообще по сути главное понимать разницу между install и update
    До того как "отправлять сделанные задачи" (только в git, а не github), сначала надо эту самую тестовую ветку завести локально. И уметь разруливать возникающие конфликты.
    Кроме того, контроль версий помогает в первую очередь тебе. Это не для дяди-тимлида, а для себя делается. Чтобы можно было посмотреть историю изменений, откатить, посмотреть какой код ты менял, когда он перестал работать.

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

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

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    гоподи, не надо никаких апачей, и уж тем более всяких опенсерверов, вампы, мампы и прочей мумбы-юмбы

    скачать пхп, распаковать, перейти в папку с пхп файлами, запустить команду
    c:\путь куда распакован РНР\php -S localhost:80
    Ответ написан
  • Проверьте метод, ничего не упустил?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Тут скорее очень много лишнего.
    Начиная с названия.
    Почему функция с названием intClean занимается и теми и другими числами?
    Зачем вырезать "случайно поставленную букву"? Зачем вообще позволять вводить буквы?
    Зачем заменять точку в интах?
    Зачем позволять точку в поле, которое требует целое число
    Зачем ловить исключение внутри функции?!

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

    В общем, надо переделать,
    сделать отдельные функции для целых и для плавающей точки.
    обе проверять на формат и диапазон
    Ответ написан
    2 комментария
  • Где и как лучше прехватывать/обрабатывать исключения?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Главная вещь, которую надо понимать про исключения, это то что они бывают двух основных видов.
    После этого вся обработка становится совершенно естественной и очевидной.

    - Error exceptions, или по простому говоря - ошибки. Обычные ошибки при выполнении программы. Обычно код бросает их сам. Решение "обрабатывать все ошибки через set_exception_handler" будет вполне логичным.
    - Business logic exceptions - это не ошибка в строгом понимании этого слова, а скорее нормальное поведение программы. Ситуация исключительная, но только для бизнес-логики. Их всегда кидает программист.

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

    - Error exceptions почти никогда не ловятся через try-catch, по крайней мере на месте. За исключением редких исключительных ситуаций обработка ошибок производится в единой точке, обработчике ошибок
    - Business logic exceptions всегда ловятся через try-catch

    Отсюда мы видим, что

    - //PDOException при коннекте (эммм... я понимаю что пример, но блин, new PDO в конструкторе репы, серьёзно?ладно, мы сейчас не об этом) - это однозначно ошибка
    - //PDOException в запросе - это тоже ошибка, тут два раза думать не надо
    - условно пустое имя. Ну вот здесь мы уже переходим в область бизнес-логики. Коду тут без разницы, пустое имя, или полное. Это важно нам - программисту, пользователю.

    Но тут есть один, блин, тонкий момент.
    Валидация, по сути, пытается разорваться между всеми слоями приложения.
    С одной стороны, это функция Сущности (которую ошибочно называют моделью) - проверять валидность своих данных.
    С другой - если нам надо донести результаты валидации до пользователя, то как быть с переводами? Тащить в модель переводчик, серьёзно? Ну ок, ладно, возвращаем ключи для перевода. Хотя тоже как-то...
    Но вот проверка емейла на уникальность. Её-то где делать?
    В Сущности? И тащить в нее соединение с БД?
    На уровне БД? А где ловить тогда исключение? В сервисе? И ломиться через несколько уровней абстракции к сырому PDOException? Не вариант.
    Или, к примеру, для модели естественно проверять каждое поле отдельно, и кидать исключение. А для пользовательского интерфейса это неприемлемо - надо выдавать все ошибки валидации скопом, а не скармливать по одной.
    Вопросы...

    Но "где валидировать данные" - это отдельная тема, которая не относится напрямую к вопросу "где ловить ошибки".

    В данном случае я предлагаю оставить Сущность Юзер без валидации, а всю валидацию делать в сервисе.

    Хотя опять же - в современных фреймворках валидацию (Не будем показывать пальцем, но это был Ларавель) вообще делают еще до запуска контроллера, в миддлвари. Это кстати спорное решение, которое нарушает целостность модели. Если мы обращаемся к модели через другую точку входа, не контроллер, а, к примеру, создаем юзера через командную строку, то нам нужна точно такая же валидация. Запускать команды через мидлварь? В сущности, это мысль... Но всё равно, мы в итоге бизнес-логику размазываем между моделью и точками входа в неё, а это костыль.
    И при всём при этом оставлять Сущность Юзер совсем без валидации тоже как-то не комильфо... А если оставлять - то получится по сути дублирование кода.
    Вопросы, вопросы...

    Но вернемся к нашим баранам, в смысле юзерам.

    Начнем с того, что проверка имени на равенство пустой строке или нулю - это какой-то детский лепет (и кстати, почему ноль нельзя? вот у Маска ребеночка зовут X Æ A-12 - почему у кого-то не может быть имя "0.0"?).

    Отдельно побурчу насчет empty. Вообще, это один из самых сложных операторов, на нем спотыкаются все поголовно. В частности,
    function f($name){
        if (empty($name))...

    - это бессмыслица. Звучит, в переводе на русский, немного шизофренически: "пусть у нас будет переменная $name. Если у нас нет переменной $name...". Ну как нет, если мы только что ее в функцию передали?
    empty() проверяет переменную на существование И "пустоту". И в данном случае первая проверка будет бессмысленной. Никогда не надо писать бессмысленный код.
    Поэтому логичнее будет написать просто if(!$name). Хотя по нынешним временам это тоже говнокод. Что мы имеем здесь в виду? Имя не может быть пустой строкой? Пустым массивом? Нулём? null? false? А true или заполненный массив - это, получается, хорошее, годное имя?
    Лучше все-таки четче определять свои претензии. К примеру, проверять длину строки.

    У имени можно сделать миллион проверок: Минимальную длину, максимальную длину. Проверить что это строка, а не массив, в конце концов, и не булево значение.
    И так по каждому полю.
    То есть, по-хорошему, валидация данных на соответствие правилам бизнес-логики - это отдельный большой бизнес!

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

    class ValidationError extends Exception{ ... };
     
    class User
    {
        private string $name;
    
        public function __construct(string $name)
        {
            $this->name = $name;
        }
    }
    
    class UserRepository
    {
        public function __construct(PDO $pdo)
        {
            $this->builder = $pdo;
        }
        public function add(User $user): User
        {
            $saved = $this->builder->query('INSERT INTO users (name) VALUES ("user")'); //PDOException
        }
    }
    
    class UserSerivce
    {
        private UserRepository $repository;
        private Validator $validator;
    
        public function new(array $data): User
        {
            $rules = [...] ;
            $errors = $this->validator->validate($data, $rules);
            if ($errors) {
                throw new ValidationException("", $errors);
            }
            $user = new User($data['name']); // в принципе, сучность может здесь бросить своё ValidationException
            return $this->repository->add($user);
        }
    }
    
    class UserController
    {
        private UserSerivce $service;
    
        public function store(array $data)
        {
            try {
                $user = $this->service->new($data);
            catch (ValidationError $e) {
                // рассказываем юзеру что он дурак
            }
            return redirect()->to('/' . $user->id);
        }
    }


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

    Как можно заметить, вот весь этот длинный и путанный текст посвящен исключительно ошибкам бизнес-логики.
    Поскольку с ошибками кода всё куда проще - единый хендлер тупо их обрабатывает в одном месте, как описано в статье по ссылке @Spartak-2205
    За исключением редких случаев, когда они ловятся по месту. Когда ошибка некритичная, или есть сценарий обработки - например, попробовать выполнить то же действие еще раз.
    Ответ написан
    3 комментария
  • Можно ли использовать die при валидации?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Просто разве это не защитит меня от человека, который просто поймёт как он генерируется.

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

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

    Я все больше склоняюсь к тому что ты тупо троллишь
    потому что ну не может человек настолько последовательно ходить по всем граблям, которые только бывают
    да еще и упорствовать в желании получать рукояткой по лбу
    Ответ написан
  • Ошибка в запросе?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    1. Надо включать отображение ошибок РНР.
    Тогда ПХП тебе бы прямым текстом сказал, что в нет никакой переменной "priceMan". Хотя если смотреть на полный запрос, то скорее отсутствует parpage
    2. Надо использовать для запросов подготовленные выражения
    Тогда даже при отсутствии переменной не будет ошибки запроса. Не говоря уже про взлом сайта

    Даже такая параша, как тухлая фасоль, и то умеет в замещение переменных в запросе

    $params = [
    'min' => $_GET['priceMin'],
    'max' => $_GET['priceMax'],
    'cnt' => $cnt,
    'start' =>$start, 
    'perpage' =>$perpage,
    ]
    $idparam = '';
    foreach ($ids as $i => $item)
    {
        $key = ":id$i";
        $idparam .= $idstr? "," : "" . $key;
        $params[$key] = $item; 
    }
    $filterparam = '';
    foreach ($ids as $i => $item)
    {
        $key = ":f$i";
        $filterparam .= $filterparam? "," : "" . $key;
        $params[$key] = $item; 
    }
    
    
    
    $sql = "SELECT id, articul, category_id, brand_id, title, alias, content, price, old_price, status, keywords, description, img, hit, novinki  
    FROM product 
    WHERE status = '1' AND category_id IN ($idparam) 
    AND price > :min AND price < :max
    AND id IN (SELECT product_id FROM attribute_product WHERE attr_id IN ($filterparam) 
    GROUP BY product_id 
    HAVING COUNT(product_id) = :cnt)
    LIMIT :start, :perpage"
    
    $rows = \R::getAll($sql, $params);
    Ответ написан
  • Почему базовый класс ловит исключение?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Это очень плохой подход.
    Все делается ради красивости, а не для реальной пользы.
    FileWriteException может случиться примерно по 100500 разных причин.
    НИ ОДНА ИЗ НИХ не будет сообщена несчастному разработчику, который будет пытаться понять, ПОЧЕМУ не получилось файл записать. Ему, как в том анекдоте, будут говорить "слушайте свою песню Валенки!", вместо реального сообщения об ошибке.

    Кидать красивости можно на уровне файлового враппера, но при этом обязательно сохранять исходное сообщение об ошибке!

    public function write($filename, $data) {
        try {
            file_put_contents($filename, $data);
        } catch (Throwable $e) {
            $e = new FileWriteException($e->getMessage());
            $e->setName($filename);
            throw $e;
        }
    }
    Ответ написан
    6 комментариев
  • Как безопасно использовать WHERE?

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

    class Test {
        protected $where;
        protected $params;
    
        public function getAll()
        {
            $result=$this->db->prepare("SELECT * FROM table WHERE 1=1 ".$this->where);
            $result->execute($this->params);
            return $result->fetchAll(PDO::FETCH_ASSOC);
        }
    
        public function byDate($start,$end=false)
        {
            if ($end) {
                $this->where .= " AND data BETWEEN :start AND :end";
                $this->params['end'] = $end;
            } else {
                $this->where .= " date >= :start";
            }
            $this->params['start'] = $start;
            return $this;
        }
    
        public function byCol($col)
        {
            $this->where .= " AND col = :col";
            $this->params['col'] = $col;
            return $this;
        }
    }
    
    $sql = new Test;
    $data = $sql->byCol($col)->byDate($start)->getAll();


    по-хорошему тут еще должны быть отдельные переменные для самого запроса, для order by, limit и так далее
    Но начать можно с такого
    Ответ написан
    3 комментария
  • Освобождается ли память?

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

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

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

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

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

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

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

    Данные не бывают "опасными" или "безопасными" сами по себе.
    Всё зависит от контекста.
    А любая обработка "просто на всякий случай" тупо испортит данные.
    К примеру твоя strip_tags() изуродует математический текст, в котором встречаются символы "больше" и "меньше".

    Поэтому и надо форматировать данные перед использованием, в зависимости от конкретного контекста, а не заранее. Используем в SQL? Применяем подготовленные выражения. Используем в HTML? Применяем htmlscpecialchars. Используем в URL? Применяем urlencode. Используем в яваскрипте? Применяем json_encode. И так далее. Тебе уже самому должно быть смешно, глядя на этот набор "базовых проверок", если их накатывать все скопом.

    Плюс не надо путать форматирование данных и их валидацию.
    Валидация, в принципе, хорошая вещь, но надо понимать что во-первых, она не имеет никакого отношения к безопасности, и не может заменить контекстное форматирование данных, а во-вторых, уж тем более не может быть "универсальной" по определению.
    Ответ написан
    5 комментариев
  • Как сделать, чтобы при удалении записей остальные id записей начинались с 0?

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

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

    Либо тебе это поле вообще не нужно, и его надо убрать, либо просто оставь его в покое и никогда не трогай его руками
    Ответ написан
    1 комментарий
  • Покритикуйте код! Какие грубые и негрубые ошибки совершил?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Ну тут скорее ни одной почти строчки нормальной.
    • exit('Ошибка подключения к базе данных!'); дважды глупость. Пользователю сайта не интересно читать, что у тебя сломалось - база данных или деньги на пиво кончились. Как программисту, тебе эта бессмысленная фраза тем более бесполезна, она ничего не говорит о том, ЧТО КОНКРЕТНО сломалось, чтобы ты мог исправить
    • if ($numRows > 0) { бессмысленный кусок кода
    • while ($row = $result->fetch_assoc()) { заменяется на $result->fetch_all(MYSQLI_ASSOC)
    • $this->connect() коннектимся каждый раз, чтобы выполнить запрос, серьёзно?
    • А когда будет еще один класс, для другой таблицы, снова будешь писать код подключения к БД? И так в каждом?
    • Почему класс для работы с "позициями" называется DBh?
    • По сути это не класс, а набор функций. Если ты уберешь красивые слова class и this, то НИЧЕГО не изменится
    • SQL инъекции кругом
    • class Handler extends Dbh ВООБЩЕ непонятно зачем



    В общем, как-то так
    dbh.php
    class Dbh
    {
        public $conn;
    
        public function __construct($config)
        {
            mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
            $this->conn = new mysqli(...$config);
            $this->conn->set_charset('utf8mb4');
        }
    
        public function preparedQuery($sql, $params, $types = '')
        {
            $types = $types ?: str_repeat('s', count($params));
            $stmt = $this->conn->prepare($sql);
            $stmt->bind_param($types, ...$params);
            $stmt->execute();
            return $stmt;
        }
    
        public function selectResult($sql, $params, $types = '')
        {
            if (!$params) {
                return $this->conn->query($sql);
            }
            return $this->preparedQuery($sql, $params, $types)->get_result();
        }
        public function selectAll($sql, $params = [], $types = '')
        {
            return $this->selectResult($sql, $params, $types)->fetch_all(MYSQLI_ASSOC);
        }
        public function selectAssoc($sql, $params = [], $types = '')
        {
            return $this->selectResult($sql, $params, $types)->fetch_assoc();
        }
        public function selectRow($sql, $params = [], $types = '')
        {
            return $this->selectResult($sql, $params, $types)->fetch_row();
        }
        public function selectCell($sql, $params = [], $types = '')
        {
            $row = $this->selectRow($sql, $params, $types);
            return $row ? $row[0] : false;
        }
    }

    position.php
    class Position
    {
        protected $dbh;
    
        public function __construct(Dbh $dbh)
        {
            $this->dbh = $dbh;
        }
        // Получаем все позциии из БД и возвращаем их в массиве $output если записей больше нуля
        public function getAllPositions()
        {
            return $this->dbh->selectAll('SELECT * FROM positions');
        }
        protected function addPosition($content)
        {
            $count = $this->dbh->selectCell('SELECT count(*) FROM positions');
            if ($count < 10) {
                $this->dbh->preparedQuery("INSERT INTO positions (content) VALUES (?)", [$content]);
            }
        }
        protected function deletePosition($id)
        {
            $this->dbh->preparedQuery("DELETE FROM positions WHERE id = ?", [$id]);
        }
        //Поиск позиций в БД по столбцу content
        protected function searchPosition($content)
        {
            $content = "%$content%";
            return $this->dbh->selectAll('SELECT * FROM positions WHERE content LIKE ?',[$content]);
        }
    }

    config.php
    return [
          'db' => [
              'host' => '127.0.0.1',
              'username' => '',
              'password' => '',
              'dbname' => '',
              'port' => 3306,
          ],
      ];


    handler.php

    $config = require 'config.php';
    $dbh = new Dbh($config['db']);
    $position = new Position($dbh);
    
    switch ($_GET['action']) {
        case 'getpositions':
            $output = $position->getAllPositions();
            echo json_encode($output);
            break;
        case 'addposition':
            $content = $_GET['content'];
            $position->addPosition($content);
            break;
        case 'getsearchpositions':
            $content = $_GET['content'];
            $output = $position->searchPosition($content);
            echo json_encode($output);
            break;
        case 'deleteposition':
            $id = $_GET['id'];
            $position->deletePosition($id);
            break;
        default:
            header("HTTP/1.0 400 Bad Request");
    }
    Ответ написан
    2 комментария
  • Как на развернуть весь сайт в index.php?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Сейчас в этом коде все поставлено с ног на голову.
    Я так понимаю, что всё затевается ради того, что "Шапка и футер сайта у меня везде одинаковые"
    И сначала идет шапка, потом этот матч, а потом футер.
    То есть у нас код начинается с вывода HTML, в то время как это должно быть самое последнее, что происходит в скрипте.
    А всё должно быть строго наоборот - сначала должна выполняться обработка данных РНР кодом, и только потом начинаться вывод HTML.

    Взять к примеру запрос /cart, который в обязательном порядке должен обрабатывать запросы POST
    после такого запроса обязательно должен выполняться редирект. А редирект мы сделать не можем, потому что у нас пол-сайта уже клиенту ушло.
    Не говоря уже о других НТТР загололовках, или о том, что хидер у тебя не статичный, и значительно меняется от страницы к странице.

    Для начала надо сделать вот так, phpfaq.ru/tech/tpl#example

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

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    В цикле.
    Все операции над массивами делаются в цикле.

    Если ты не знаешь, как перебрать в цикле массив чисел, тебе надо учить циклы, а не ООП.
    Ответ написан
    Комментировать
  • Как правильно указать относительные и абсолютные пути?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Все твои проблемы из-за уродливого костыля в виде site. От него надо избавляться.
    Каждый виртуальный сервер должен быть в своей папке. Тогда ты сможешь писать везде нормальные абсолютные пути от корня сайта

    Подробнее про абсолютные и относительные пути, phpfaq.ru/newbie/paths
    Ответ написан
    Комментировать
  • Как понять есть ли нагрузка на БД?

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

    Так вот: я смотрю, что получил 1500 строк из базы за 0.0316 секунд. Это нормально?

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

    База растёт и строк к выводу станет больше.

    С КАКОГО, я стесняюсь спросить, перепугу, с ростом базы строк к выводу станет больше?
    На тостере с каждым днем прибавляется сотня дебильных вопросов.
    Ты уверен что количество запрашиваемых из базы строк тоже растёт? А если подумать? А если прям вот хорошенько подумать?

    Этот ход мысли напоминает старый еврейский анекдот, который рассказывал Джоэл Спольский в далёком 2001 году:
    Маляр Шлёма подрядился красить пунктирные осевые линии на дорогах. В первый день он получил банку краски, поставил её на дорогу, и к концу дня покрасил 300 метров осевой линии. «Отлично! — сказал прораб. — Быстро работаешь!» и заплатил ему.
    На следующий день Шлёма покрасил 150 метров. «Мда, это, конечно, не так здорово, как вчера, но приемлемо», — сказал прораб и снова заплатил ему.
    Ещё через день Шлёма покрасил всего 30 метров. «Всего лишь 30! — заорал прораб. — Это никуда не годится! В первый день было в десять раз больше! В чём дело?»

    «Ничего не могу поделать, — говорит Шлемиэль. — Каждый день я ухожу всё дальше и дальше от банки!»


    Тебе не кажется что эта логика напоминает твоё "но с каждым днём в БД появляется всё больше и больше записей!"?

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

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

    И вопрос в догонку: если одним запросом я получаю 1500 строк и если в запросе я установлю select нужных мне столбиков - это усложняет запрос или нет? Ведь в первом случае я получаю информацию как есть, а во втором случае процессору надо время чтобы перебрать нужные столбики?!

    Судя по количеству восклицательных знаков - это самый важный вопрос во всём этом и так целиком гениальном тексте. Процессор опасносте!!! Срочно надо спасать!

    Запрос выполняется три сотых секунды, дом рисуется 10, но вопрос почему-то "как узнать , не тормозит ли база?"
    Ну ей-богу, снова как в анекдоте - "Где логика??! Где разум??".

    Сейчас я кеширую результаты php на 60 минут,

    Вот это я понимаю. Сразу заходим с козырей.
    странно что на 60 минут, а не на 24 часа. или вообще сделать сайт статикой. тогда вообще всё летать будет. Или вообще перенести всю БД на клиента. Чего не сделаешь ради борьбы за миллисекунды.

    Как понять есть ли нагрузка на БД?/

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

    А вместо всяких "оптимизаций" типа кэширования на 60 минут запроса, который выполняется пару сотых секунды, или не на покупки бессмысленного диска, а на букварь про работе с БД. И прочитать там про нормализацию, индексы, базовые команды SQL, пагинацию, в конце концов.

    И тогда и твоя микроскопическая БД в 10 тыщ записей, и нормальная база с миллионами строк, будут работать одинаково быстро и эффективно.
    Ответ написан
    8 комментариев