VueJS + PHP. Как это всё подружить?

Здравствуйте, уважаемые разработчики. Мне нужна ваша помощь.
Всё свое время занимался front-end разработкой и в back-end никогда не лез. Но разрабатывая свой проект, встал вопрос о написании back-end части web-приложения. В качестве front-end использую VueJS + vuex + vue-router. Мне требуется сделать справочник рецептов с фильтром, который отправляет выбранные пользователем теги на сервер, т.е их получает php (это можно сделать через Axios, да?). На серверной стороне идет обработка тегов и получив данные из БД, отправляет их в vue.js компонент, а потом vue рендерит их на странице , правильно я понимаю? То есть страниц с рецептами может быть много и при переходе на каждую страницу js взаимодействует с php и наоборот, и в компоненте они отображаются. Хватит ли для этого vue-router или нужен другой роутер?
И ещё 2 вопроса, front-end и back-end должен полностью быть разделен? Как можно организовать структуру папок, используя при этом сборщик WebPack?
Прошу прощения, если где имеются глупые вопросы или догадки, но мне очень важны ответы на них. Использование Firebase для моего проекта не подходит, так как в будущем будет достаточно сильное расширение проекта.
  • Вопрос задан
  • 19098 просмотров
Решения вопроса 1
@marsdenden
vue-router сам по себе, роутер на php - сам по себе, никак между собой не связаны. Если от бэкенда нужны только данные и никакого дополнительного функционала, я бы попробовал сделать сервер на ноде, благо модули готовые есть, по крайней мере писать на одном языке проще и эффективнее, чем на двух (имхо). А так все просто - фронт использует axios, в который передается url, параметры и callback для обработки ответа. Бэк обрабатывает вызов и отправляет JSON с данными и на этом его миссия закончилась, фронт в колбэке получает json и делает свое дело.
Например, как я это сделал (метод - всегда POST)

в App.vue в methods
callApi(url,prms,callback){
  this.setServerError('',''); // это функция, которая в data выставляет определенные поля
  //в результате чего ошибки выводятся прямо на странице, удобно для отладки
  axios({
    method:"post",
    url:url,
    data:prms
  }).then((response) => {
    // в response.data получаем JSON, 
    // в моем случае сервер формирует обязательные поля success,error,buffer
    // в buffer  перед выдачей JSON снимается html-вывод, возможно это отладочная информация,
    // которую выдает backend, возможно PHP-warnings
    let dt=response.data;
    if(!dt.success){
       	this.setServerError(dt.error,dt.buffer);
    }else{
        // ну и, собственно, сам вызов колбека, который происходит только в случае успешного приема данных
        callback(dt);
    }
  }).catch((error) => {
    // эту часть вызывает сам axios при возникновении серверных ошибок, то есть все, что не 200 OK 
    // позволяет увидеть, в частности, ошибку 500, вернее сам факт ее возникновения, если она обрабатывается
    // "стандартным" методом апача - пустая страница и все
    this.setServerError(error.message,error.stack);
  });
},



далее в created создается подписка, можно использовать Bus.js, у меня по другому

this.$root.$on('callApi',(url,prms,callback)=>{this.callApi(url,prms,callback)});


ну и в компоненте или где еще в проекте делается вызов

this.$root.$emit('callApi', '/api/goods', {action: 'getgoodsinfo', article: '12345678'}, (dt) => {
         this.articleinfo=dt.data.articleinfo;
      })


на бэкэнде, соответственно, есть api.php, в котором происходит разбор url, чтение параметров и формирование результата

заготовка api.php
<?php
// регистрируем функцию завершения, чтобы обрабатывать грубые ошибки, 
//например вызов несуществующего метода у объекта
register_shutdown_function(function () {
    $error = error_get_last();
    if ($error && ($error['type'] == E_ERROR || $error['type'] == E_PARSE || $error['type'] == E_COMPILE_ERROR)) {
        $res=array(
            'buffer'=>ob_get_contents(),
            'success'=>false,
            'error'=>"PHP Fatal: ".$error['message']." in ".preg_replace('/(.*)\/(.*)/', "$2", $error['file']).":".$error['line']
        );
        ob_clean();
        header('HTTP/1.1 200 Ok');
        header("Access-Control-Allow-Origin: *");
        echo json_encode($res);
        // ... завершаемая корректно ....
    }
});

// для кроссдоменного CORS, при необходимости - закомментировать или заменить звездочку на требуемое
if($_SERVER['REQUEST_METHOD']=='OPTIONS' ){
    ob_clean();
    header("Access-Control-Allow-Origin: *");
    header("Content-type: application/json; charset=utf-8");
    header("Access-Control-Allow-Headers: X-Requested-With, Content-Type");
    header("Access-Control-Request-Method: POST");
    return true;
}

// /*
// протокол обмена
// - вход - команда по сегменту, например http://site.ru/api/goods - api - попали сюда, goods - команда REST
// в php://input должен быть json, в котором обязателен параметр action, например getgoodsinfo
// в результате формируется имя функции класса goods_getgoodsinfo, которая вызывается 
// с параметром входящего json

// функция класса должна вернуть массив с тремя полями - data & success & error
// в поле data возвращается непосредственно результат функции, в нашем случае - реестр чеков
// в поле success возвращается true | false - признак успешного выполнения
// в поле error возвращается описание ошибки в случае неудачного выполнения функции
// */

$api=new ApiCls();

// функция, проверяющая залогиненность юзера
if(is_user_login()){ 
    $api->checkcommand();
}else{
    $res=array('succes'=>false,'error'=>'Пользователь не авторизован','data'=>'');
    ob_clean();
    header("Content-type: application/json; charset=utf-8");
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: X-Requested-With, Content-Type");
    echo json_encode($res);
}

return true;

class ApiCls{
    function checkcommand(){ // точка входа для класса api, здесь первичный разбор, 
        //вызов метода и возврат результата
        // главное - определить второй сегмент в url, то есть в случае http://site.ru/api/goods
        $segment=????????; // в $segment должен оказаться 'goods'

        $res=array('success'=>false,'error'=>'Empty action'); // сразу проверка на наличие action в параметрах
        if(!$segment){
            $res['error']='Empty command';
        }else{
            if($_SERVER['REQUEST_METHOD']=='PUT' || $_SERVER['REQUEST_METHOD']=='POST'){
                $reqdata = file_get_contents('php://input'); 
                $b=json_decode($reqdata);
                $b=get_object_vars($b);
                if(isset($b['action'])){
                    $res['error']='no error';
                    $nm=$segment.'_'.$b['action'];
                    $r=$this->$nm($b); // вызов метода по action из пост и дальнейшая обработка результатов
                    // чё-то тут намутил, но работает - и ладно
                    if(!isset($r['success'])){
                        $res['success']=false;
                        $res['error']='No success flag in method '.$nm;
                    }else{
                        if(!isset($r['data'])){
                            $res['success']=false;
                            $res['error']='No result data in method '.$nm;
                        }else{
                            $res['success']=$r['success'];
                            $res['data']=$r['data'];
                            if(!$r['success']){
                                if(isset($r['error'])){
                                    $res['error']=$r['error'];
                                }else{
                                    $res['error']='Success is false, but no error message in method '.$nm;
                                }
                            }
                        }
                    }
                }
            }
        }
        // непосредственная выдача данных
        $this->_printresponse($res);
    }
    function _printresponse($res){
        // проверяем наличие html-вывода (отладка или warnings)
        $res['buffer']=ob_get_contents();
        if($res['buffer']!=''){
            $res['success']=false;
            isset($res['error'])?$res['error']=implode(',',array('module error',$res['error'])):$res['error']='module error';
        }
        // очищаем буфер вывода и формируем свои заголовки
        ob_clean();
        header("Content-type: application/json; charset=utf-8");
        header("Access-Control-Allow-Origin: *");
        header("Access-Control-Allow-Headers: X-Requested-With, Content-Type");
        // ну и, наконец, выдаем результирующий JSON
        echo json_encode($res);
    }
    function __call($name,$post){
        // эта функция вызывается при попытке вызвать несуществующий метод класса, то есть при вызове '/api/blablabla' получим отлуп
        $info=array('method'=>$name,'post'=>$post,'error'=>'Method not found','success'=>false);
        return $info;
    }
    /*********************************************************/
    // начинаем блок методов api
    function goods_getgoodsinfo($prm){
        // формируем заготовку ответа
        $res=array('data'=>array(),'success'=>true,'error'=>'');
        // при необходимости здесь можно вставить проверку наличия полномочий у пользователя
        // и если что не так, то выключить success, прописать "облом!" в error и не возвращать данные
        $res['data']['articleinfo']= getArticleInfo($prm['article']); // вызов функции, 
        //которая собирает требуемые данные для определенного артикула и возвращает, опять же
        // в формате JSON
        return $res;
    }
}
?>



Вот как-то так... Дальше только вопрос - как прикрутить этот api.php к сайту и вопросы безопасности, но это уже другая тема
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
be_a_dancer
@be_a_dancer
Backend/Fullstack Developer
Рекомендую посмотреть, как это дело реализовано в Laravel, который по умолчанию содержит готовый и настроенный Vue.js модуль и тесную с ним интеграцию.
Разделять стоит, хотя это и не обязательно.
А почему именно PHP? Ты же знаешь JS. Можешь на ноде поднять свой сервер.
Ответ написан
mQm
@mQm
https://medium.com/@gearmobile
Мне также кажется, что лучшим вариантом будет Vue+Laravel. И хоть я сам Laravel пока не владею (изучаю Nuxt.js), но в будущем планирую изучить этот фреймворк. На фрилансе чуть ли не половина заказов по Vue - это конкретно Vue+Laravel.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы