Мешанина VS MCV или Теория Баланса

в 8:08, , рубрики: php, говнокодинг для личных нужд, ненормальное программирование, метки: ,

Здравствуйте, люди добрые!

Если мы зайдем на сайт говнокод.ру, то мы увидим, что количество постов с плохим кодом в разделе PHP более чем в 3 раза опережает ближайших конкурентов. Естественно, на это есть свои причины — низкий порог вхождения, простота языка, наличие готовых решений, позволяющих не юзать мозг облегчающих решение типичных задач и т.д… Иногда мне даже становится обидно за репутацию языка и остальных php-программистов.

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

Условия

Возьмем два проекта — скрипт, функционал которого описан выше, и базу данных, о которой я так же вкратце упомянул. И условно назовем первый маленьким проектом, а второй — большим.
Особенности маленького проекта:

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

Особенности большого проекта:

  • Постоянное расширение функционала
  • Специфичность задачи, как следствие — отсутствие готовых решений
  • Приемлемый бюджет
Как все начинается

Думаю, я не ошибусь, что большинство из нас любит интересные задачи. Но как быть, когда тебя просят о помощи знакомые люди? Вроде бы за плечами уже не один серьезный проект, довольные пользователи и несколько лет опыта, но интернет так тесно врос в нашу жизнь, что к нам никогда не перестанут обращаться по мелочам («у моей тёти интернет-магазин кормов для кошек, и ей нужна возможность оплачивать заказ безналом»; «давай сделаем соцсеть, у меня оригинальная идея и через пару месяцев мы будем миллионерами»). Конечно, можно убедить их в том, что они безумны что им это на самом деле не надо, а можно помочь в надежде, что когда-нибудь нам помогут в ответ. Получаем, что и вознаграждение не особо большое, потому что по знакомству, и мотивация одна — сделать быстрее и оставить еще один говносайт, о котором все забудут через пару месяцев, на просторах интернета.

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

Начинаем писать код

Начну с описания большого проекта. Я четко понимал, что работать над ним мне придется одному, данные должны храниться в безопасности, приложение должно получиться расширяемым и легко переносимым с одного сервера на другой или с одной платформы на другую. А еще все должно быть максимально упрощенно для пользователей, поэтому пришлось дополнительно принимать меры по фильтрации и обработке вводимых данных, вести подробные логи, сделать интуитивно понятный интерфейс с расчетом на то, что пользоваться им будет умственноотсталая мартышка после лоботомии.
Дополнительно был настроен сервер mysql с репликацией и мониторинг серверов через zabbix с оповещением на почту mail.ru и последующим смс. Все пишется в стиле MCV, чтоб не запутаться и с легкостью вносить изменения в каждый модуль и чтоб система целиком не рухнула в случае непредвиденного косяка одного из модулей.

Теперь по маленькому проекту. Такого словосочетания, как «техническое задание», человек, попросивший меня приделать к его сайту отправку электронной почты, не знал. Поэтому очень смутно с утра он написал мне, что бы он хотел видеть в итоге и во всем остальном доверился мне. Ок, сделаем как хочешь. Срок — 12 часов.

Прелестный выходной, чудесная погода, а к вечеру 2 новые серии любимых сериалов. В итоге на маленький проект у меня осталось 3 часа.

Казалось бы, времени более чем достаточно, чтобы вставить функцию отправки сообщения, добавления в таблицу в БД, нацепить bootstrap в некое подобие админки и распрощаться с этим клиентом до скончания веков, но врожденное умение находить лишний геморрой на свою голову будет моим вечным поставщиком проблем. Я решил сделать приличную админку с авторизацией, с настройками отправителя сообщения, редактирования тела и темы сообщения пользователю, оставившему заявку, приделать валидацию форм на клиентской и на серверной стороне. С учетом лени и оставшегося времени — странные мысли.

Мешанина VS MCV

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

Большой проект:

Каждый модуль независим друг от друга. Даже если мы удалим файлы, связанные с пользователями (не из БД, разумеется) система все равно продолжит работать в штатном режиме.
В каждом модуле мы имеем такие файлы:
Controller_name,php
Model_name.php
-templates
action_one.php
action_two.php

default.php

Может так и не принято делать, но в данной ситуации это удобно и логично.
В итоге получаем что если пользователь попадет в пункт
/?item=cars&actions=view_last_week
загрузится модуль Cars и подключится шаблон view_last_week.php

Маленький проект:

В начале была вписана такая конструкция:

$action=isset($_GET['action']) ? $_GET['action'] : NULL;

switch ($action) {
    case 'read':
        $admin->updateStatus($_GET['id']);
        break;
    case 'delete':
        $admin->deleteMessage($_GET['id']);
    default:
        $admin->view();
        break;
}

Теперь что у нас в классе $admin

public function view() {

        $messages = $this->getMessages();
        ?>
        <table>
            <thead>
            <th>id</th>
            <th>Номер телефона</th>
            <th>Адрес почты</th>
            <th>Имя</th>
            <th>Статус</th>
        </thead>
        <tbody>
            <?php
            foreach ($messages as $message) {
                ?>
                <tr>
                    <td><?php echo $message['id']; ?></td>
                    <td><?php echo $message['phone']; ?></td>
                    <td><?php echo $message['mail']; ?></td>
                    <td><?php echo $message['name']; ?></td>
                    <td><?php echo $message['status']; ?></td>       
                    <td><a href="?action=read&id=<?php echo $message['id']; ?>">Прочитано</a></td>
                    <td><a href="?action=delete&id=<?php echo $message['id']; ?>">Удалить</a></td>
                </tr>
                <?php
            }
            ?>
        </tbody>
        </table>

        <?php
    }

    public function getMessages() {
        $query = $this->db->prepare('select * from messages');
        $query->execute();
        $row = $query->fetchAll(PDO::FETCH_ASSOC);
        return $row;
    }

    public function updateStatus($id) {
        $query = $this->db->prepare('UPDATE  messages SET `status` = ? WHERE  id =?');
        $query->execute(array('Прочитано', $id));
        $this->view();
    }

    public function deleteMessage($id) {
        $query = $this->db->prepare('DELETE FROM messages WHERE id = ?');
        $query->execute(array($id));
    }

Чем это плохо? Тем, что здесь класс, отвечающий за выборку сообщений и обновление их статуса выводит html код (молчу про отстутствие комментариев и про то, что этот класс отвечает вообще за все в данном контексте). Если бы мне пришлось дорабатывать нечто подобное после кого-нибудь, я мог бы пристрелить этого человека и меня бы оправдали.

А что хорошего? Получается некая имитация динамического удаления или обновления статуса сообщения. Т.е. после нажатия на ссылку «Прочитано» страница обновится и соответствующие данные уже будут в БД, а при удалении отобразится уже новый список. Хотя, конечно, лучше было бы посылать ajax-запрос, который в фоне сделал бы всю работу и убрал бы лишний запрос к БД.

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

Теория баланса

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

Автор: MetaDone

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js