«Hello, (real) world!» на php в 2017 году

    Вы наверняка думаете, что писать на php — это просто. И «hello, world» выглядит примерно так так:

    <?php
    echo 'Hello, world!';
    

    Конечно, чего еще ожидать от языка с низким порогом входа. Ну да, именно так и было раньше. Много лет назад. Но теперь, в 2017 году никто так уже не делает. Давайте рассмотрим, почему, и попробуем построить наше более реалистичное hello-world приложение по шагам, а их, скажу сразу, получилось не мало.


    → Полный исходный код «hello,world» можно посмотреть здесь.

    Для начала надо осознать тот факт, что без фреймворка сейчас приложения никто не делает. Если вы пишете вручную "echo 'hello, world'", то обрекаете проект на говнокод на веки вечные (кто потом этот велосипед за вас переписывать будет?). Поэтому возьмем какой-нибудь современный, распространенный в мире фреймворк, например Symfony.

    Но прежде, чем его устанавливать, надо бы создать базу данных. Зачем базу данных? Ну не хардкодить же строку «hello, world» прямо в тексте программы!

    База данных


    В 2017 году принято использовать postgresql. Если вы вдруг еще не умеете его устанавливать, я помогу:

    sudo apt-get install postgresql

    Убунта при установке создаст юзера postgres, из под которого можно запустить команду psql с полными правами на базу.

    sudo -u postgres psql

    Теперь создадим юзера базы с паролем (придумайте какой-нибудь посложнее).

     CREATE ROLE helloworlduser  WITH PASSWORD '12345'  LOGIN;

    И саму базу:

     CREATE DATABASE helloworld  OWNER helloworlduser;

    Также надо убедиться, что в pg_hba.conf у вас разрешены коннекты к базе с localhost (127.0.0.1). Там должно быть что-то вроде этого:

    host    all             all             127.0.0.1/32            md5

    Проверим соединение:

    psql -h localhost -U helloworlduser helloworld

    после ввода пароля должно пустить в базу. Сразу создадим таблицу:

    CREATE TABLE greetings (
         id int,
         greeting text,
         primary key(id)
    );
    
    INSERT INTO greetings 
    (id, greeting) 
    VALUES 
    (1, 'Hello, world!');
    

    Ну, супер, с базой всё. Теперь перейдем к фреймворку

    php-фреймворк


    Надеюсь, что в 2017 году у всех стоит composer на компьютере. Поэтому сразу перейдем к установке фреймворка

     composer create-project symfony/framework-standard-edition  helloworldphp

    При установке он сразу спросит параметры соединения с базой:

    host: 127.0.0.1
    database_name: helloworld
    database_user: helloworlduser
    database_password: 12345

    остальное по умолчанию/по усмотрению.

    Надо только в конфиге config.yml поменть драйвер на driver: pdo_pgsql. (У вас ведь установлено php-расширение pdo_pgsql ?)

    Проверим, что всё более менее работает, запустив

    cd helloworldphp
    bin/console server:start 
    

    Симфони запустит свой собственный сервер, который слушает порт 8000 и на нем можно дебажить код. Таким образом в браузере по адресу http://localhost:8000/ должно быть что-то вроде «Это симфони, блаблабла».

    Уфф! Казалось бы всё, контроллер уже есть, подправить вьюху, создать модель и понеслась, хелло ворлд уже близко!

    Но… нет. Извините, но не в 2017-ом. В этом году все делают SPA (single page application).
    Php-программист в 2017 году не может обойтись без js и верстки, теперь мы все full stack, а значит и helloworld должен быть соответствующий.

    Ну ладно, ладно, еще бывают чистые php-бекенд-разработчики, но давайте возьмем более общий случай

    JavaScript и его многочисленные друзья


    Поэтому находим в симфони вьюху (а дефолтная вьюха лежит в app/Resources/view/default/index.html.twig) и стираем там всё, заменяя на:

    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="utf-8">
       <meta http-equiv="X-UA-Compatible" content="IE=edge">
       <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    <div id="root"></div>
    <script src="js/bundle.js"></script>
    </body>
    </html>

    Т.е. всё будет лежат в bundle.js: сжатые javascript файлы прямо вместе со стилями и всем, чем нужно.
    Как нам создать этот бандл? Нужно написать приложение и настроить webpack для сборки.

    Webpack (или его аналоги) нам все равно бы понадобились, мы же не будем писать код на чистом javascript в 2017-году, когда typescript явно в тренде. А typescript надо как-то преобразовать в обычную js-ку. Это удобно делать, используя webpack.

    Разумеется, на чистом typescript тоже никто не пишет. Нужен какой-то фреймворк. Одна из самых модных связок сейчас — это react + redux. А для верстки, так и быть, будем использовать старый добрый олдскульный bootstrap (через sass, конечно же).

    Нам понадобится куча js-библиотек. У вас ведь стоит nodejs и npm? Убедитесь, что у вас свежий npm и установите пакеты:

    npm init 

    в зависимостях (в файле package.json) пропишем примерно такое:

    "dependencies": {
     "@types/react": "^15.0.11",
     "@types/react-dom": "^0.14.23",
     "babel-core": "^6.23.1",
     "babel-loader": "^6.3.2",
     "babel-preset-es2015": "^6.22.0",
     "babel-preset-react": "^6.23.0",
     "bootstrap-sass": "^3.3.7",
     "css-loader": "^0.26.1",
     "node-sass": "^4.5.0",
     "react": "^15.4.2",
     "react-dom": "^15.4.2",
     "react-redux": "^5.0.2",
     "redux": "^3.6.0",
     "resolve-url-loader": "^2.0.0",
     "sass-loader": "^6.0.1",
     "style-loader": "^0.13.1",
     "ts-loader": "^2.0.0",
     "typescript": "^2.1.6",
     "url-loader": "^0.5.7",
     "webpack": "^2.2.1",
     "@types/node": "^7.0.5"
    }


    И выполним

    npm install

    и еще нужно установить:

    npm install webpack -g

    чтобы была доступна команда webpack.

    Увы, это еще далеко не всё. Так как у нас typescript, еще надо создать файл tsconfig.json, примерно такой:

    tsconfig.json
    {
      "compilerOptions": {
        "module": "es6",
        "moduleResolution": "node",
        "sourceMap": false,
        "target": "esnext",
        "outDir": "web/ts",
        "lib": [
          "dom",
          "scripthost",
          "es5",
          "es6",
          "es7"
        ],
        "jsx": "react"
      },
      "include": [
        "frontend/**/*.ts",
        "frontend/**/*.tsx"
      ]
    }


    С конфигами пока что ок, теперь займемся нашим приложением на typescript.

    Сначала создадим компонент для отображения нашего текста:

    // файл frontend/components/Greetings.tsx
    import * as React from 'react';
    
    export interface GreetingsProps {
        text: string;
        isReady: boolean;
        onMount();
    }
    
    class Greetings extends React.Component<GreetingsProps, undefined> {
    
        componentDidMount() {
            this.props.onMount();
        }
    
        render() {
            return (
                <h1>{this.props.text}</h1>
            );
        }
    }
    
    export default Greetings;

    Наше SPA будет подгружать текст надписи через Rest API. React — это просто view-компоненты, а нам еще нужна логика приложения и управление состоянием.

    Так что будем использовать redux, а также пакет для связи redux и react (react-redux). Поэтому надо будет еще создать компонент, который будет создавать наш компонент Greetings с нужными properties, и сможет сообщить хранилищу (store) состояния, что появилось новое действие (получены данные для отображения).

    Disclaimer: я только начал изучать redux, поэтому наверняка тут есть за что «бить по рукам».

    Выглядит этот компонент, допустим, примерно так:

    // файл frontend/components/App.tsx
    import * as React from 'react';
    
    import {connect} from 'react-redux'
    import Greetings from './Greetings';
    
    
    const mapStateToProps = (state) => {
        return state;
    }
    
    const mapDispatchToProps = (dispatch) => {
        return {
            onMount: () => {
                fetch("/greetings/1").then((response) => {
                    return response.json();
                }).then((json) => {
                    dispatch({type: 'FETCH_GREETING', text: json.greeting})
                });
            }
        }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(Greetings);

    Ну и точка входа приложения, создание redux-стора, диспатчера и т.д. Тут всё сделано немного по рабоче-крестьянски, но для хелловорлда сойдет, пожалуй:

    // подгружает стили  bootstrap
    import 'bootstrap-sass/assets/stylesheets/_bootstrap.scss';
    
    import * as React from 'react';
    import * as ReactDOM from "react-dom";
    import {Provider} from 'react-redux';
    import App from './components/App';
    import {createStore} from 'redux';
    
    const app = (state = {isReady: false, text: ''}, action) => {
        switch (action.type) {
            case 'FETCH_GREETING':
                return Object.assign({}, state, {isReady: true, text: action.text});
        }
        return state;
    }
    
    const store = createStore(app);
    
    ReactDOM.render(
        <Provider store={store}>
            <App/>
        </Provider>,
    
        document.getElementById("root")
    );

    Примерно здесь происходит следующее:

    • Первоначальное состояние системы — {isReady: false, text: ''}.
    • Создан reducer под названием app, который умеет обрабатывать действие FETCH_GREETING и возвращать новое состояние системы.
    • Создан store для обработки состояний.
    • Всё отрендеривается в элемент, который мы прописали во вьюхе <div id="root"></div>

    Ах да, совсем забыл. Конфиг вебпака:

    webpack.config.js
    const webpack = require('webpack');
    const path = require('path');
    const ENVIRONMENT = process.env.NODE_ENV || 'development';
    
    let config = {
        context: path.resolve(__dirname, "frontend"),
        entry: './index.tsx',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, "web/js")
        },
        resolve: {
            extensions: [ ".js", ".jsx", '.ts', '.tsx']
        },
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    use: [{
                        loader: 'babel-loader',
                        query: {
                            presets: ['es2015', 'react']
                        }
                    }, {
                        loader: 'ts-loader'
                    }]
                },
    
                {
                    test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/,
                    loader: 'url-loader'
                },
    
                {
                    test: /\.scss$/,
                    use: [
                        {
                            loader: "style-loader"
                        },
                        {
                            loader: "css-loader"
                        },
                        {
                            loader: "resolve-url-loader"
                        },
                        {
                            loader: "sass-loader"
                        }
                    ]
                }
            ]
        },
        plugins: [
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify(ENVIRONMENT)
            })
        ],
        node: {
            process: false
        }
    };
    
    if (ENVIRONMENT == 'production') {
        config.plugins.push(
            new webpack.optimize.UglifyJsPlugin({
                compress: {
                    drop_console: false,
                    warnings: false
                }
            })
        );
    }
    
    module.exports = config;
    


    Теперь мы можем запустить webpack или NODE_ENV=production webpack (чтобы получить минифицированную версию bundle.js)

    Pomodoro


    Не знаю как вы, а я уже задолбался писать этот hello, world. В 2017 году надо работать эффективно, а это подразумевает, что надо делать перерывы в работе (метод Pomodoro и т.д.). Так что, пожалуй, прервусь не надолго.

    [прошло какое-то время]

    Давайте продолжим. Мы уже умеем подгружать код с /greetings/1 на стороне javascript, но php-часть еще совершенно не готова.

    Doctrine


    Уже потрачено много времени, а в php-коде не создано ни одной сущности. Давайте исправим положение:

    <?php
    
    // src/AppBundle/Entity/Greeting.php
    namespace AppBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity
     * @ORM\Table(name="greetings")
     */
    class Greeting
    {
        /**
         * @ORM\Column(type="integer")
         * @ORM\Id
         */
        private $id;
    
        /**
         * @ORM\Column(type="string", length=100)
         */
        private $greeting;
    
        public function getId()
        {
            return $this->id;
        }
    
        public function getGreeting()
        {
            return $this->greeting;
        }
    }
    

    Супер. Осталось совсем чуть-чуть.

    REST


    Надо сделать-таки простенький REST API, который может хотя бы отдать json по запросу GET /greetings/1

    Для этого в контроллере (файл src/AppBundle/Controller/DefaultController.php) добавим метод с роутом:

        /**
         * @Route("/greetings/{id}")
         */
        public function greetings($id)
        {
            $greeting = $this->getDoctrine()->getRepository("AppBundle:Greeting")->find($id);
            return new JsonResponse(['greeting' => $greeting->getGreeting()]);
        }

    Всё, можно запускать. На экране отображается «Hello, world!». Внешне он, конечно, выглядит почти также как результат <?php echo «hello, world» ?> (если не считать бутстраповского шрифта), но теперь это современное приложение по всем канонам. Ну, скажем так, почти по всем канонам (не хватает тестов, проверок ошибок и много чего еще), но я уже задолбался это делать :)

    Выводы


    В последнее время сильно участились споры «зачем нужен php, если есть java». Уж не знаю, кто прав, а кто нет, холивары — дело такое. Но в каждом споре один из аргументов в пользу php — это простота для новичков. Как мне кажется, этот аргумент уже давно не валиден, что я и хотел показать этой статьёй. Новичку все равно придется кучу всего узнать и 100500 конфигов настроить: фреймворки (очень похожие на фреймворки java), базы данных, linux, javascript со всем своим зоопарком, верстка, http-протокол, различный тулинг и многое-многое другое. Даже если это не SPA.

    Upd. Статья уходит в глубокий минус, но я не собираюсь менять мнение. Оно примерно такое:
    1) SPA всё больше проникает в наш мир, и надо это уметь, хотя бы в общих чертах.
    2) Без фреймворков не построишь хорошее современное приложение.
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 571
    • +16
      Вы смешали frontend (где действительно избыточно много абстракций) и backend.
      Достаточно было создать модель и вывести «Hello world!» из базы во вьюхе через контроллер.
      • +1
        Согласен, получилось скорее hello, world SPA. Но сейчас SPA со страшной силой шагает по планете. Зайдите в любую современную админку банка и т.д. — там будет реакт. Мне кажется, надо уметь хотя бы хелловорлд всего этого дела.
        • +1
          SPA должен умереть…
          • +4

            отчего же? Это же волшебно когда UI на клиенте это не статическая страничка а луп отрисовывающий стэйт! А если еще и выстроен как композиция реюзабельных компонентов, ух… А бэкэнд stateless и в целом не зависит от UI особо, крути верти как хочешь. Дикий простор для творчества. Для определенного процента проектов можно вообще без бэкэнда обходиться благодаря всяким там firebase-ам и похожим сервисам. Идеально для CRUD-like приложений, да и с кодогенерацией для UI и реюзом оного как-то получше дела обстоят.


            Конечно же это не для всех проектов/команд подходит… И надо фронтэнды уметь, с чем сейчас тугова-то у людей. То есть свои жирные минусы несомненно есть...

          • 0
            Мне не нравится JSX из-за смешивания логики (кода) и шаблона. В Knockout.js, который я использую, логика отдельно, шаблон data-bind отдельно. Это позволяет использовать общий шаблон с разными JS классами, что очень удобно. Никаких транспилеров и кучи node.js приложений не нужно, все работает на ES5 без какой-либо прекомпиляции. Сам Knockout.js очень небольшой и содержит все что нужно.

            SPA плохи двумя вещами — слабой индексацией в поисковиках и глюками старых браузеров. Я больше использую AJAX компоненты чем чистое SPA. Хотя для админки SPA действительно хорош — там не нужна индексация и вряд ли кто зайдет старым браузером.
            • +1
              Я раньше также думал. Пока не попробовал.

              В JSX есть логика, но это только view логика.

              Когда вы в handlebars пишете
                {{#if author}}
                  <h1>{{firstName}} {{lastName}}</h1>
                {{/if}}
              


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

              Разумеется, бизнес-логику нельзя пихать в react-компонент, ни в коем случае. Для бизнес-логики надо юзать redux (или еще какой-нибудь flux), обмениваясь с компонентами событиями и т.д.
              • 0
                Разумеется, бизнес-логику нельзя пихать в react-компонент, ни в коем случае.

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

                • –1
                  Когда вы в handlebars пишете

                  Так не пишите, оставьте шаблоны простыми.

                  • 0

                    Ну вот кстати именно поэтому на первый взгляд менее удобные mustache в итоге оказываются более поддерживаемым решением. Ибо вся логика оттуда искусственно вытеснена в контроллеры.

                  • +2
                    из-за смешивания логики (кода) и шаблона.

                    Twig/Nunjucks/Jinja2


                    <ul>
                    {% for item in list %}
                       <li>{{ item}}</li>
                    {% else %}
                       <li class="empty">List is empty :(</li>
                    {% endfor %}
                    </ul>

                    Angular


                    <ul>
                      <li *ngFor="let item of list">{{item}}</li>
                      <li class="empty" *ngIf="!list.length">List is empty :(</li>
                    </ul>

                    React JSX


                    render: function() {
                        return (
                            <ul>
                              {this.props.list.map(item => <li>{item}</li>)}
                              {this.props.list.length == 0 &&
                                 <li class="empty">List is empty :(</li>
                              }
                            </ul>
                          )
                        }

                    Knockout.js


                    <li data-bind="foreach: list">
                       <span data-bind="text: $data"></span>
                    </li>
                    <li class="empty" data-bind="if: pets().length == 0">List is empty :(</li>

                    Я это к чему — логика отображения в шаблонах — от этого вы не уйдете. Разница лишь в способах которыми вы это дело разруливаете. JSX это лишь сахар чтобы удобнее было.


                    все работает на ES5 без какой-либо прекомпиляции.

                    Я уже не могу писать просто на es5 без всяких бабелев… к хорошему быстро привыкаешь.


                    SPA плохи двумя вещами — слабой индексацией в поисковиках и глюками старых браузеров.

                    На счет браузеров — согласен, хотя с каждым годом ситуация меняется в лучшую сторону. Ну а по поводу индексации — приведу пример. angular-universal. Server-side рендринг в пол пинка (ну как. относительно конечно).

                    • 0
                      Тем не менее в случае knockout / angular мы имеем самый минимум логики в шаблонах (конечно можно запихнуть в data-bind JS-код на несколько строк, только это даже человеку не любящему красоту кода будет очевидно выглядеть плохо), а react как-бы поощряет запихивать побольше кода в шаблон. Понятно что хороший программист будет сдерживать себя.

                      И еще момент с наследованием этого дела. В ES5 и тем более в ES6 можно прототип метода у потомка заменить и пожалуйста — тот же шаблон knockout.js с другим кодом. В то же время в популярном Vue.js с наследованием оказались огромные проблемы вообще не рекомендуют использовать. Хотя на мой взгляд UI без наследования это нон-сенс.

                      es5 хорош тем что быстрее проект разворачивать (node.js и прочее не нужны) а также сторонний код можно на лету править.

                      На react может конечно и надо переходить так как спрос на него огромен, хотя идеологически коробит меня от него, привыкнуть ко всему можно. Куча уже существующего кода на Knockout.js работает и вроде как непонятно зачем. Разве что из-за денег.
                      • +1
                        Тем не менее в случае knockout / angular мы имеем самый минимум логики в шаблонах

                        присмотритесь. Количество логики ровно одинаковое. Разница лишь в синтаксисе. Причем в этом плане у jsx есть преимущество перед knockout — не надо изучать новый синтаксис — все работает на обычном js. Как и в underscore templates например и куче других шаблонизаторов.


                        es5 хорош тем что быстрее проект разворачивать

                        yoman, кучи скафолдингов, шаблоны проектов и т.д. решают эту проблему. А за счет es2015+ скорость разработки и удешевление поддержки быстро окупает эти вещи.


                        На react может конечно и надо переходить

                        В целом я фанат ангуляра так что… я не перейду)

                      • +2
                        React версию можно отрефакторить до такого:

                        render() {
                            let {list} = this.props;
                            return (
                                <ul>
                                    {list.map(item => <li>{item}</li>)}
                                    {!list.length && <li className="empty">List is empty :(</li>}
                                </ul>
                              )
                        }
                        
                        • 0

                          да, так поприятнее. Тогда разница вообще ничтожна.

                        • 0

                          $mol:


                          $my_pets $html_ul
                              Pet_row!index $html_li
                                  sub /
                                      <= pet_name!index \
                              No_pet $html_li
                                  sub /
                                      <= no_pet_messsage @ \Not pets :(

                          sub() {
                              if( this.items().length === 0 ) return [ this.Empty() ]
                              return this.pets().map( ( name , index )=> this.Pet_row( index ) )
                          }
                          
                          pet_name( index : number ) {
                              return this.pets()[ index ]
                          }

                          На таком простом примере преимуществ не видно, но обычно каждая строка — это более сложная штука, так что разделение логики и композиции компонент (аналог шаблонов к компонентной архитектуре) не просто возможно, но и позволяет обуздать сложность, появляющуюся, когда логика идёт вперемешку с кусками html.

                          • 0

                            Не this.Empty(), а this.No_pet(), конечно же. И с наследованием тут всё хорошо (прям отлично). И нет, это не "самопиар", а объяснение, как та же проблематика может быть решена лучше.

                            • 0
                              Ваш пример не особо лучше. Вы все равно изувечили html, напихав туда свой язык. Как и в других шаблонизаторах
                              • 0

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

                            • 0
                              На таком простом примере преимуществ не видно

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

                              • 0

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


                                При разделении композиции и логики вы просто пишете, например:


                                $my_app $mol_view
                                    as_card true
                                    sub / <= Card $mol_card
                                        sub / <= Info $mol_labeler
                                            title <=  info_title @ \Your salary
                                            content / <= Salary $my_currency
                                                value <= salary null

                                А потом добавляете отдельно любой сложности логику:


                                sub() {
                                    return [ this.as_card() ? this.Card() : this.Info() ]
                                }
                                • +1
                                  На любом более-менее реальном.

                                  То есть вывести список — не реальная задача?


                                  Ну, банальная задача: в зависимости от флага заворачивать содержимое в дополнительную обёртку.

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

                                  • 0

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


                                    Так тут родитель (блок $my_app) и управляет всеми своими элементами (Card, Info, Salary).

                                    • 0
                                      Вывести список строк в одиночном теге — не реальная

                                      Очень реальная. Пример — вывести табличку с заголовком строки в виде ФИО клиента и одним из столбцов через запятую номера заказов.

                                      • 0

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

                                        • 0

                                          Сложно по бумаге кликать.

                                          • 0

                                            Версия для печати — очень частный случай.

                                            • 0

                                              Большинство отчётов не нуждаются в интерактивности, даже если формально не печатаются.

                                              • 0

                                                У нас ровно противоположная статистика.

                                                • 0

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

                                                  • +2

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


                                                    В целом же как по мне ваш варинт излишне сложен если сравнивать его с любым другим.

                                                    • 0
                                                      Значит ваша выборка "реальных случаев" не может быть репрезентативной.

                                                      А какая может? Будет чудесно, если вы приведёте ссылку на репрезентативную статистику.


                                                      Да и вывести ссылку — не сильно сложная проблема

                                                      Я говорил про не только ссылку.


                                                      "Мутация" контейнера детьми я расцениваю больше как какой-то костыль

                                                      Тут нет никакой мутации контейнера. Тут вывод либо одного компонента, либо другого. И это вполне штатная ситуация для нормального инструмента.


                                                      В целом же как по мне ваш варинт излишне сложен если сравнивать его с любым другим.

                                                      Реализуйте мой пример по проще :-)

                                                      • –1
                                                        Реализуйте мой пример по проще :-)

                                                        <my-card *ngIf="asCard()"></my-card>
                                                        <my-info *ngIf="asInfo()"></my-info>

                                                        Если я правильно понимаю о чем ваш пример.

                                                        • 0

                                                          Нет, типичная реализация того же на Ангуляре будет выглядеть так:


                                                          <my-card *ngIf="asCard()">
                                                              <my-info>много тегов</my-info>
                                                          </my-card>
                                                          <my-info *ngElse>копипаста</my-info>
                                                          • +1
                                                            типичная реализация того же на Ангуляре будет выглядеть так:

                                                            Эм...


                                                            1. в Angular нет ngElse. Это атрибут элемента, он ничего не знает о соседях.
                                                            2. Вся "копипаста" выносится в отдельный компонент. И компонент-родитель будет рулить какой выводить и с какими параметрами.

                                                            Ну то есть мэйнстрим подход, UI как композиция UI компонентов.

                                                            • 0
                                                              1. Тем более, значит ещё и условие повторить надо.
                                                              2. Ну то есть вводится специальный компонент со своим конфигом, с пробрасыванием всех параметров. И это не потому, что требуется декомпозиция в этом месте, а потому, что шаблонизатор настолько дубовый, что не способен переставить блок в другое место.
                                                              • –1
                                                                1. не то что бы повторить, у вас должен быть красивый и удобный метод во вью модел который проверяет это условие, и там где надо просто надо добавить отрицание. Логика провери же будет инкапсулирована.


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

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

                                                                • +1
                                                                  1. Суть от этого не меняется — нужно повторить вызов метода. А если надо будет привязаться к другому методу, то оба вызова надо будет поправить.


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

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

                                                                  • –1
                                                                    А если надо будет привязаться к другому методу, то оба вызова надо будет поправить.

                                                                    Как жаль что при построении логических выражений нет возможности делать отрицания....


                                                                    Именно, поэтому в "шаблонах" вообще не должно быть логики

                                                                    так ее и нет, вся логика в презентерах.


                                                                    От того, что вы разобьёте один компонент на два сильносвязанных

                                                                    компоненты не могут быть "сильносвязанными". Они либо имею связи/зависимости либо нет.

                                                                    • 0

                                                                      В одно месте вам нужно написать "asCard()", в другом "!asCard()". А если нужно первый блок показать при выполнении условий А и Б, второй — Б и В, а третий — А и С? Да, вы можете создать отдельные методы вида "isFirstBlockShowing", но:


                                                                      1. так мало кто сделает.
                                                                      2. писанины будет ещё больше.

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


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

                                                                    • +1
                                                                      Выносить в отдельный компонент имеет смысл лишь переиспользуемую логику.

                                                                      Очень спорное утверждение, противоречащее многим общепринятым принципам проектирования типа того же принципа единственной ответственности.

                                                                      • 0

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

                                                                        • –1
                                                                          не несёт какой-то уникальной ответственности.

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


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


                                                                          Ангуляр не фосирует вынос логики, а наоборот поощряет её написание прямо в шаблоне.

                                                                          в целом — да, но можно во вью модели логику подублировать всегда. У разработчиков всегда будет возможность сломать инкапсуляцию и продублировать логику. С этим надо бороться другими способами.


                                                                          Могут. Выделяя как попало компоненты вы получаете такие вложенные

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

                                                                          • 0

                                                                            Ок, пусть это будет карточка товара: Фотка, название, описание, цена, список тегов. На всех страницах товары показываются такими карточками, но на некоторых страницах карточка должна быть ссылкой, а на некоторых — нет.

                                                                            • 0

                                                                              Решение в лоб


                                                                              ProductCard = (product) => 
                                                                                <div>
                                                                                  <ProductImage value={product.image}/>
                                                                                  <ProductName value={product.name}/>
                                                                                  <ProductDescription value={product.description}/>
                                                                                  <ProductPrice value={product.price}/>
                                                                                  <ProductTags value={product.tags}/>
                                                                                </div>;
                                                                              
                                                                              LinkableProductCard = ({product}) => <a href={product.link}><ProductCard product={product}></a>;
                                                                              • 0

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

                                                                                • –1

                                                                                  Всё строго по ТЗ :) Единственное замечание которое принимаю сходу — title в ссылку, но только как фичереквест. О всём остальном вы не можете судить по этому фрагменту, не зная схемы объекта product. А div не лишний, а несёт семантическую нагрузку — отделяет карточку продукта от остального документа, делает её самостоятельным элементов, на который, в числе прочего, можно ссылаться в файлах стилей. И компоненты лишними не считаю, даже если они не будут переиспользоваться — они улучшают поддерживаемость кода. Вот захотели вы валюту указывать — я джуну скажу "там есть элемент ProductPrice — добавь наименование валюты", а без компонентов придется "там есть семиэтажный Product, поройся в нём, найди часть, где цена выводится и добавь наименование валюты"

                                                                                  • +1

                                                                                    ТЗ изменилось. Сколько вам времени потребуется на рефакторинг всего проекта под новые требования, описанные в предыдущем сообщении?


                                                                                    Вы на имя тега будете ссылаться в файлах стилей?


                                                                                    Для отображения валют в разных местах имеет смыл создать компонент CurrencyValue. Отображать ProductPrice вне ProductCard было бы странно.

                                                                                    • 0

                                                                                      title для ссылки откуда брать? product.name или product.link.title? Если первое, то до десяти минут, включая деплой на стейдж- сервер. Если второе — ждём бэкенедеров и плюс 10 минут.


                                                                                      Пока в проекте только одно место, где нужно отображать. Появится второе — будем смотреть что в них общего.

                                                                                      • 0

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

                                                                                        • 0
                                                                                          карточка должна быть ссылкой

                                                                                          Согласно ТЗ и реализовано. Никаких "внутри" в ТЗ нет. Ели опять ТЗ меняется, то уточните, что значит "внутри". И почему не могу завернуть ссылку в ссылку?

                                                                                            • 0
                                                                                              • 0

                                                                                                image

                                                                                                • 0

                                                                                                  Работает?

                                                                                                  • 0

                                                                                                    Нет, работать оно будет либо в режиме XHTML, либо если вы создаёте DOM скриптом. О каком-либо изоморфном рендеринге HTML в таком случае придётся забыть.

                                                                                                    • 0

                                                                                                      В ТЗ что-то было про изоморфный рендеринг? По умолчанию у нас обеспечивается только функциональность в актуальной стабильной версии Chrome, что фиксируется в коммерческом предложении на внедрение и SLA, ну в Firefox потестим чтобы вёрстка не плыла глобально, а практически весь body генерируется скриптами, клиенту отдаётся минимальная статическая страница.

                                                                                                      • 0

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


                                                                                                        Вы когда к стоматологу приходите — тоже выкатываете ему подробное тз или всё же рассчитываете на его профессионализм и понимание что и для чего он делает и к каким последствиям это приведёт?

                                                                                                        • 0

                                                                                                          Не за нами написанным, за вами.

                                                                                                          • 0

                                                                                                            У вас видимо очень продвинутые заказчики, раз сами пишут вам ТЗ :-)

                                                                                                            • 0

                                                                                                              Мне не важно кто пишет, наши заказчики или наши аналитики, главное, что я не пишу.

                                                                                                        • –1
                                                                                                          В ТЗ что-то было про изоморфный рендеринг?

                                                                                                          Справедливости ради, вот эти "в тз небыло" немного напрягают. А как же protected variations?

                                                                                                          • 0

                                                                                                            Небольшие (с точки зрения заказчика) могут полностью перевернуть подход к разработке. Навскидку, для изоморфного рендеринга React-"шаблонов" я вообще никаких оценок дать не могу — знаю, что некоторые люди это как-то делают, вероятно с помощью http-сервера на NodeJS. Всё. Есть ли у React различия между рендерингом в DOM и в http-ответ я не знаю, в этом направлении в компании вообще ничего не делалось никогда. И вообще, может из-за требования изморфного рендеринга нужно будет полностью пересмотреть бэкенд часть. Ну или оставить PostgreSQL, но выпилить из проекта PHP и меня вместе с ним.


                                                                                                            Обеспечивать во фронтенд коде совместимость с каким-то сервером, которого разработчик в глаза не видел — это не мелочь, которую можно опустить в ТЗ по принципу "это же очевидно, вы же профессионалы"

                                                                                                            • 0

                                                                                                              Представьте, что у заказчика уже есть изоморфное приложение, вам нужно лишь добавить раздел с товарами. Если вы не будете учитывать спецификации, то обретёте проблемы с тем, как браузеры и роботы понимают генерируемый вашим кодом HTML.

                                                                                                              • 0

                                                                                                                Для такой задачи я увеличу сроки на месяц, даже если оценю её чисто для фронта на час. И то с большими рисками не уложиться.

                                                                                                                • 0

                                                                                                                  А я увеличу на неделю и уведу у вас заказчика :-Р

                                                                                                        • 0
                                                                                                          Вы нам лучше объясните какой плюс заказчику от изоморфного рендеринга :)
                                                                                                          • 0

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

                                                                                                            • 0

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


                                                                                                              Продать изоморфность весьма просто. И в целом это не требует каких-то больших эфортов если делать нормально.


                                                                                                              @vintage я вот только ваш кейс с "а тепер весь блок продукта стал ссылкой" немного не понял. Мне всегда казалось что делать блоки ссылками — не хорошо, и как правило была бы ссылка где-то внутри блока а какой-то декоратор над контейнером уже бы превращал весь блок в ссылку. Это как бы и кейс изоморфности покрывает, и делать легко и просто, и нет конфликтов со спецификациями.

                                                                                                              • +1
                                                                                                                Ну например «хотите чтобы ваш круто интернет-магазин вообще поисковиками индексировался»


                                                                                                                А он индексируется только при изоморфном рендеринге? :)
                                                                                                                Или других способов отдавать выдачу не за 5 секунд нет?

                                                                                                                Продать-то можно что угодно — вон участки на Луне вполне себе продают…
                                                                                                                Но реальный плюс какой?
                                                                                                                • 0
                                                                                                                  А он индексируется только при изоморфном рендеринге? :)

                                                                                                                  Быстрее и надёжнее индексируется, если рендерится на сервере. Но для этого не обязательно делать изоморфный рендеринг, есть способы попроще, типа prerender.io.


                                                                                                                  Или других способов отдавать выдачу не за 5 секунд нет?

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

                                                                                                                  • 0
                                                                                                                    Мы же о веб-приложениях и мобильных приложениях говорим?

                                                                                                                    В первом случае там голый html, который давно собирался на сервере.
                                                                                                                    Во втором случае могут быть варианты, вплоть до VNC.

                                                                                                                    Я просто, читая вашу дискуссию, до сих пор не осознал всех радостей.
                                                                                                                    Судя из всего — изобрели трудности и собираем страницу нетрадиционными средствами. Из-за чего проблемы с индексацией и т.п.

                                                                                                                    То есть ровно те проблемы, которых не имеют традиционные server-side языки — java, php, python, ruby, etc…

                                                                                                                    А потом борьбу с этими проблемами выдаем за преимущество каких-то решений.

                                                                                                                    Я правильно понял общую мысль?

                                                                                                                    • 0

                                                                                                                      Веб приложения можно открывать и с мобильника.


                                                                                                                      На старом голом хтмл вы не сделаете отзывчивого приложения с удобным интерфейсом. с внс — тем более.


                                                                                                                      Проблема с индексацией не сложно решается.


                                                                                                                      Традиционные языки имеют кучу других проблем. Основная из которых — они не работают в оффлайне.

                                                                                                                • 0

                                                                                                                  Я не очень понял, чем то, что вы описали отличается от реализации VolCh.

                                                                                  • 0
                                                                                    ListItemsOrNoItems = ({items}) => items.length !== 0  ? <ListItems items={items}/> : <NoItems/>;
                                                                                    
                                                                                    ListItems ({items}) => <ul>{items.map((item) => <ListItem item={item}/>)}</ul>;
                                                                                    
                                                                                    ListItem = ({item}) =>  <li>{item}</li>;
                                                                                    
                                                                                    EmptyList = () => <p>No items</p>;
                                                                                    

                                                                                    У каждого компонента ровно одна собственная уникальная ответственность. Будет она переиспользоваться где-то, может будет дублироваться — дело десятое. Главное — декомпозиция ответственностей и обязанностей.

                                                                                    • 0

                                                                                      Замечательно. А если мне нужен ListItemsOrNoItems, но с другими ListItems и NoItems?

                                                                                      • 0

                                                                                        Что-то вроде
                                                                                        ListItemsOrNoItems = (items, AbstractListItems, AbstractNoItems) => items.length !== 0 ? <AbstarctListItems items={items}/> : <AbstarctNoItems/>;


                                                                                        и вызов codehtml


                                                                                          <ListItems items={items}  AbstractListItems={ListItems} AbstractNoItems={NoItems/} />

                                                                                        P.S. что-то разметка глючит

                                                                                        • 0

                                                                                          Очень наглядно, спасибо.

                                                                                          • 0

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

                                                                                            • 0

                                                                                              Чудесно, особенно когда таких опциональных компонентов больше 2.

                                                                                              • 0

                                                                                                Это же XML по сути.

                                                                                                • 0
                                                                                                  ListItemsOrNoItems = (items, AbstractListItems, AbstractNoItems) => items.length !== 0 ? <AbstarctListItems items={items}/> : <AbstarctNoItems/>;

                                                                                                  Это лапша. И в жизни это так не работает, так как вы не прокидываете параметры в в создаваемые тут компоненты.

                                                                                                  • 0

                                                                                                    Присмотритесь — прокидываю. И в чем лапша?

                                                                                                    • 0

                                                                                                      Вы прокидываете один единственный параметр — items.

                                                                                                      • 0

                                                                                                        От количества параметров что-то зависит? Тем более их можно группировать в объекты типа options

                                                                                                        • 0

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

                                                                                                          • 0

                                                                                                            Чем оно не переиспользуемо? Вывод списка элементов или элемента, замещающего пустой список — типовая задача и данный сниппет её решает. Передачу дополнительных параметров (что обычно для таких задач не требуется, стили или инлайнятся/импортятся в конкретных элементах, или через контекст передается тема) для дочерних элементов можно реализовать разными способами, например через…

                                                                                                            • 0

                                                                                                              Договаривайте уж. Через контекст? Глобальную переменную? Урл?

                                                                                                              • +1

                                                                                                                Это не многоточие русского языка, а рест оператор джаваскрипта/джейэсикс :)

                                                                                                                • 0

                                                                                                                  что ж вы сразу-то этого не сделли? Давайте посмотрим какой код у вас получится.

                                                                                                                  • 0

                                                                                                                    Для сравнения, у меня получился такой.


                                                                                                                    Рендерит списки любых компонент, а если список пустой — рендерит опять же произвольный компонент переданный владельцем. При этом не трубется никаких прокидываний параметров через...

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

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

                                                                                                                    • 0

                                                                                                                      Не видел необходимости. KISS

                                                                                                                      • 0

                                                                                                                        Теперь она появилась, не заставляйте иеня упрашивать вас написать полноценное переиспользуемое решение, которое можно применять в реальных ситуациях, а не только в комментариях на Хабре :-)

                                                                                                                        • 0

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

                                                                                                                          • 0

                                                                                                                            Ну, я же не поленился, и сделал продакшен решение по максимальным критериям переиспользуемости. Потратил на это (включая написание демонстрационного примера) не более 5 минут. Неужели на реакте это потребует сильно больше времени? Как же так?

                                                                                                                            • 0

                                                                                                                              Сформулировать требования к переиспользуемости много времени займёт. Навскидку, рассматривать ли возможность замены тегов ul, li и т. п.

                                                                                                                              • 0

                                                                                                                                Разумеется :-)


                                                                                                                                • замена тегов, аттрибутов, стилей, полей как корневого элемента так и всех вложенных
                                                                                                                                • рендеринг разных типов строк вперемешку
                                                                                                                                • произвольный блок вместо пустого списка
                                          • +1
                                            SPA
                                            слабой индексацией в поисковиках

                                            Как правило, приложение, в отличии от сайтов и порталов, не нуждается в индексации.

                                      • +9
                                        А если я не знаю PHP, хочу его изучить, мне сразу начинать с фрейморвка? Мне кажется более логично изучить голый PHP.
                                        • 0
                                          Согласен. Но при этом не надо обманываться насчет того, что голого php хватит для реального приложения. Поэтому почти сразу же надо начинать юзать фреймворк
                                          • +4
                                            сейчас да пожалуй, но как же раньше писались веб-приложения, когда фреймворков не было
                                            • +19
                                              Сперва приходилось писать свой фреймворк. А потом на нём писать веб-приложение.
                                              • +1

                                                тысячами разработчиками писались одни и те же вещи, часто с одними и теми же ошибками. Попробуйте написать такое же (желательно так же с объектом Greeting, получаемым из базы, а не массивом или stdClass) приложение на голом PHP и количество кода вас неприятно удивит. Или в коде будут грубые ошибки.

                                                • 0
                                                  Строчек 5-6 займет? :)
                                                  • +1
                                                    Ага, но только реализация класса Greeting (без учета взаимеодействия с БД)
                                                    • +1
                                                      Это только при условии, что стоит цель усложнять код. В ином случае готовый json получается 1 запросом из базы и делать из него внутренний обьект php вообще надобности нету ;)
                                                      • +1
                                                        При условии не усложнять объект и база лишнии, пишем константу в коде, но это выше выяснили.
                                                        • 0

                                                          Не только. Условием может быть моделирование предметной области, в которой есть сущность "Приветствие".

                                                          • 0

                                                            скорее всего это будет VO

                                                            • +2

                                                              Надо побеседовать с экспертами :)

                                                • +6

                                                  Это смотря какое приложение. Да и фреймворки разные есть. Тут больше важно что нужно знать современные концепции и стандарты современного веб приложения. Вот composer — да; PSR'ы; Неймспейсы; Автолоадинг; Система контроля версий; Единая точка входа; Паблик директория; Роутинг; Шаблонизация; Query builder. Остальное имхо опционально.


                                                  И если уж говорить о самой статье, то замените symfony на laravel и react на vue. И статья выйдет короче)

                                                  • +8
                                                    Что за бред?
                                                    Часто видел когда люди сразу начинали учить cms или фреймворк и потом даже не понимали где cms, а где сам php.
                                                    • +1
                                                      голого php хватит для реального приложения

                                                      Его то хватит, вопрос в стоимости решения и покрываемых кейсах.

                                                      • 0
                                                        Пожалуй, стоит подбирать инструмент подходящий под задачу, а не бездумно хватать фреймворк, если задача запросто решается без него.
                                                        Брать фреймворк для вывода hello world — извращение.
                                                        • 0

                                                          Как минимум, нужно ещё подумать как задача будет изменяться в обозримом будущем.

                                                          • 0
                                                            Вероятность того, что задача «Hello World» менять не будет несколько выше, чем вероятность её изменения, на мой взгляд. Если задача «Hello World» должна будет вырасти дальше за пределы, собственно вывода на экран надписи «Hello World», то проще код вывода «Hello World» выкинуть и решить задачу с нуля отдельным проектом за отдельную сумму.

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

                                                              Да ладно не желают. Желают, но узнав сколько оно стоит (или в случае внутреннего заказчика — сроки), то свои аппетиты умеряют. Но возможность развития закладывать нужно практически всегда, особенно если заказчик уже рассказал о своих будущих хотелках.

                                                              • 0
                                                                Да ладно не желают. Желают, но узнав сколько оно стоит (или в случае внутреннего заказчика — сроки), то свои аппетиты умеряют.

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

                                                                  Разработчики не желают? Да нам дай волю, мы ИИ в хелловорлд запихаем, не то что СУБД с фреймворком :), да менеджеры нудят "строки-сроки".

                                                                  • 0
                                                                    Видимо я в душе менеджер, а не кодер :)
                                                                    Ну или просто сферический лентяй
                                                      • +2
                                                        ИМХО, следует сначала учить чистый PHP, потом попробовать написать свой фреймворк (все должны через это пройти, чтобы исчезло желание писать велосипеды), и только потом пробовать уже существующие фреймворки.
                                                        • +1
                                                          потом попробовать написать свой фреймворк

                                                          Тут можно напороться на проблему "а что писать то". Ну то есть мы хотим на практике поучиться — это похвально. Но как новичку определить что он делает что-то дельное? Мы же таким образом можем вредные привычки сформировать у человека.

                                                          • 0

                                                            Может выложить свое творчество на Хабр — тут очень доброжелательно ему объяснят, что он делает не так.

                                                            • +4
                                                              Практика показывает влосипедные статьи покрыты минусами от и до.
                                                              После этого у человека отпадет желание, что либо писать и в лучшем случае только на хабр. Адекватная критика потеряется в волне хейт-хайпа.
                                                              • +1

                                                                В целом есть еще чатики всякие, тостеры и подобное. Ну то есть получать фидбэк сейчас есть где. Другой вопрос что… то о чем я писал немного о другом.


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


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


                                                                Ну и под "прикладными задачами" я подразумеваю не бложики, а что-то интереснее. Например — клоны инстаграммов, твиттеров. Какие-то утилитки. Хороший пример в чатиках проскакивал — человек отрабатывал навыки на примере тулы для генерации чейджлогов из git log.

                                                                • 0
                                                                  Я это понимаю. Но мой коментарий был к VolCh.
                                                                  П.С.
                                                                  Я сам писал фреймворк, да и сейчас сталкиваюсь с несколькими самописными.
                                                                  Но они все написаны с целью решать определенные задачи.
                                                          • 0
                                                            думаю что написание своего фреймворка можно заменить изучением основных паттернов, включая mvc и внедрении зависимостей.
                                                            • +4

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

                                                            • 0
                                                              Я бы сказал, сначала основы языка, потом фреймворк, потом попробовать повторить работу фреймворка. Так будет знание того что нужно, и понимание как оно работает.
                                                              • +1

                                                                еще нюанс — следует попробовать поработать хотя бы с двумя фреймворками. Чтобы посмотреть разные подходы.

                                                                • 0

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

                                                                  • 0

                                                                    точно не ассемблер… эрланг например.

                                                            • 0
                                                              Мне кажется более логично изучить голый PHP.

                                                              А еще было бы неплохо поучиться тесты писать до момента когда надо фреймворк брать. Ну и не стоит на голом пыхе заниматься коммерческой разработкой (если у вас нет достаточно опыта).


                                                              А так да, знание фреймворка не убережет вас от незнания языка. Логику мы все ж на php пишем.

                                                              • 0
                                                                Советую изучить Python вместо PHP. Не смотря на то что PHP в последних версиях взял очень много из Питона, по легкочитаемости кода и красоте синтаксиса уступает последнему. Python по сути это тот же PHP, но лучше. Особенно если 3.x и с использованием Jinja2.
                                                              • +4
                                                                >надо осознать тот факт, что без фреймворка сейчас приложения никто не делает.
                                                                Вот поэтому о PHP и JavaScript многие такого плохого мнения — на каждый чих свой фреймворк. Все они разные. Все велосипедные и…
                                                                >кто потом этот велосипед за вас переписывать будет?
                                                                • –4

                                                                  Дело не JavaScript, а разнообразии платформ и браузеров.

                                                                  • 0

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

                                                                  • +23
                                                                    И всё же, hello world на PHP выглядит именно так:
                                                                    <?php
                                                                    echo 'Hello, world!';
                                                                    
                                                                    • +53
                                                                      он выглядит вот так:
                                                                      Hello, world!
                                                                      

                                                                      потому что для вывода строки не нужно открывать пхпшный тег вообще.
                                                                      • +2
                                                                        Просто автор- все еще master programmer, а вы — уже гуру
                                                                    • +11

                                                                      не увидел обвеса статьи в "sarcasm"

                                                                      • +31
                                                                        Автор умничал-умничал и в итоге написал всю бизнес-логику прямо в контроллер. Молодец.
                                                                        • +3

                                                                          Доставание объекта из репозитория не бизнес-логика. Здесь бизнес-логики по сути нет вообще, только бизнес-данные.

                                                                          • –1
                                                                            В 2017 прямое обращение к репозиторию и сущности из контроллера — это oldschool.
                                                                            Надо было создать сервис greetings, который орудует приветствиями, а в контроллере извлечь его из контейнера и запросить у него нужное приветствие.
                                                                                /**
                                                                                 * @Route("/greetings/{id}")
                                                                                 */
                                                                                public function greetingAction($id)
                                                                                {
                                                                                    $greetingsManager = $this->get('greetings'); 
                                                                                    $greeting = $greetingsManager=>get($id);
                                                                                    return new JsonResponse(['greeting' => $greeting]);
                                                                                }