През последната година участвах в три големи проекта. Моята задача беше да се отдалеча от старата архитектура, базирана на PHP и генериране на HTML от страна на сървъра, и преминаване към REST API.
Със стария подход се очакваше разработчиците да знаят много повече за потребителския интерфейс и визуалните аспекти на приложението. Поради това те трябваше да обърнат внимание на различни сегменти на приложението, вместо да се фокусират върху основната си цел. Наличието на API на заден план, строго отделено от потребителския интерфейс, позволи на нашите разработчици да се съсредоточат върху качеството на своя код.
Също така, тестването на API услуги е много по-лесно, тъй като REST API може да бъде проверено чрез автоматизация модулно тестване .
Имах известен опит в писането на собствена рамка, както и работа с Yii, CakePHP, CodeIgniter, Slim Framework, Symfony и няколко други рамки с отворен код. Всеки път изпитвах липса на функционалност или неудобен подход към някои проблеми.
Използвах Laravel в продължение на четири месеца, преди да реша да го избера като платформа за следващия ни проект. Самият проект имаше голям успех и тази статия е плод на този опит. Сега мога да се нарека а Разработчик на Laravel .
Вече посочих някои от моите причини за използването на Laravel и моя опит, така че нека разгледаме по-отблизо какво направи Laravel по-добър избор за най-новия ми проект:
Ядрото на Laravel се хоства на GitHub . Ядрото изпълнява IoC модел позволявайки персонализиране и пренаписване на която и да е част от рамката (заявка, регистриране, удостоверяване и др.).
Дизайнерите на Laravel не прекарват твърде много време, за да преоткрият колелото. Много решения и практики се прехвърлят от други рамки. Добър пример за този подход е разширената конзола Symfony, наречена Занаятчия , който е интерфейс на командния ред, включен в Laravel.
Маршрутът в Laravel е невероятен; много прилича на внедряването на Ruby on Rails (RoR), което много ми харесва. Можете лесно да групирате маршрути, да създавате ресурси за CRUD страници, да прикачвате филтри и автоматично да свързвате модели към параметрите на заявката.
Вложените маршрути са много полезна функция:
Route::group(['prefix'=>'level0'], function(){ Route::get('/', array('uses' => ' [email protected] ')); Route::group(['prefix'=>'/{level0}/level1'], function(){ Route::get('/', array('uses' => ' [email protected] ')); Route::post('/{custom_variable}/custom_route', array('uses' => ' [email protected] _route')); }); });
Версирането може да се приложи като група на най-горното ниво за всички вложени маршрути, като се използва префиксът ‘v1’. Когато променим версията на API, можем да запазим старата и да използваме префикса ‘v2’, за да започнем изпълнението на маршрути с нов код и логика, т.е. нови препратки към контролери и действия.
Нека разгледаме стъпка по стъпка всичко, използвано в този урок на Laravel:
Дефинирана група маршрути с път level0
на най-високо ниво на нашия API.
Ако имаме повикване BASEURL/level0
, тогава Laravel ще го разреши и ще извика метод level0()
на TestController за обработка на заявката.
безплатни хакнати кредитни карти с пари по тях 2017 г
Имаме подгрупа с {level0}/level1
модел. За достъп до ресурсите на API в тази група трябва да използваме пътя за родителската група (level0)
и съответстват на модела на нашата подгрупа ({level0}/level1)
. Например, level0/777/level1
е правилният път за тази подгрупа на нашия API. Тук имаме 777 като променлива level0, която Laravel ще прокара до манипулатора на маршрутите в подгрупата.
В крайна сметка имаме два примерни маршрута:
BASEURL/level0/777/level1
- GET заявката към този URI ще бъде обработена с метод level1($level0)
на TestController, където първият параметър е {level0}
и тя ще бъде инициализирана със стойност 777.
BASEURL/level0/777/level1/888/custom_variable
- POST заявката към този URI ще бъде обработена с помощта на custom_route($level0, $custom_variable)
метод на CustomController.
Параметрите, използвани в метода, идват от променливите на маршрута.
И накрая, можем да свържем нашата променлива {level0}
в маршрута с модел, т.е. LevelModel. В този случай рамката автоматично се опитва да намери съществуващи записи на база данни и да ги предаде като параметър на метода на контролера.
Помага ни да напишем по-малко код и не е нужно да пишем LevelModel::find($id)
или LevelModel::findOrFail($id)
в контролери.
The Dingo API пакет прави маршрутизация още една стъпка.
Ето няколко функции, които Dingo предоставя на рамката:
API::user()
Laravel се основава на Eloquent ORM. Използвал съм го с PostgreSQL и MySQL и в двата случая се е представил безупречно.
Официалната документация е изчерпателна, така че няма причина да се повтарят нещата в тази статия:
Обхват на заявката - логиката на заявката се създава като функция със специален префикс на обхвата.
Следващият пример показва стандартната SQL заявка, трансформирана във функция за заявка в Eloquent ORM:
SELECT * WHERE hub_id = 100 AND (name LIKE `%searchkey%` OR surname LIKE `%searchkey%`):
function scopeByFieldListLike($query, $fields, $value){ $query->where(function($query) use ($fields, $value){ foreach($fields as $field){ $query->orWhere($field , 'like', '%'.$value.'%'); } }); return $query; }
Използването на функция във вашия код е лесно.
$model = new User; $model->byFieldListLike(['name', 'surname'], 'searchkey');
Много красноречиви методи връщат екземпляр на QueryBuilder. Можете да използвате почти всички тези методи на модели.
Писането на модулни тестове обикновено отнема много време, но определено си заслужава времето, така че, моля, направете го.
Laravel разчита на TestCase базов клас за тази задача. Той създава нов екземпляр на приложението, разрешава маршрута и изпълнява метода на контролера в собствената си пясъчник. Той обаче не изпълнява филтри за приложения (App :: before и App :: after) или по-сложни сценарии за маршрутизиране. За да активирам тези филтри в среда на пясъчник, трябваше да ги активирам ръчно, като използвам Route::enableFilters()
.
Очевидно е, че ако знаете как да използвате Laravel, знаете, че може да се наложи още малко работа в сегмента за тестване на модули. Настроих няколко функции, които ми помогнаха да създам по-усъвършенствани модулни тестове. Чувствайте се свободни да ги вземете и да ги използвате във вашите проекти.
За да извърша по-напреднали тестове в реалния живот и да изпълня заявки, подобни на къдрици, използвах библиотеката kriswallsmith / buzz. Това ми предостави така необходимия набор от функции за тестване, включително персонализирани заглавки и качване на файлове.
Кодът по-долу показва функцията, която можете да използвате за изпълнение на този вид тест:
public function browserRequest($method, $resource, $data = [], $headers = [], $files = []) { $host = $this->baseUrl(); if (count($files)){ // Use another form request for handling files uploading $this->_request = new BuzzMessageFormFormRequest($method, $resource, $host); if (isset($headers['Content-Type'])) { // we don't need application/json, it should form/multipart-data unset($headers['Content-Type']); } $this->_request->setHeaders($headers); $this->_request->setFields($data); foreach($files as $file) { $upload = new BuzzMessageFormFormUpload($file['path']); $upload->setName($file['name']); $this->_request->setField($file['name'], $upload); } $response = new BuzzMessageResponse(); $client = new BuzzClientFileGetContents(); $client->setTimeout(60);//Set longer timout, default is 5 $client->send($this->_request, $response); } else { $this->_request = new BuzzMessageRequest($method, $resource, $host); $this->_request->setContent(json_encode($data)); $this->_request->setHeaders($headers); $response = new BuzzMessageResponse(); $client = new BuzzClientFileGetContents(); $client->setTimeout(60);//Set longer timout, default is 5 $client->send($this->_request, $response); } return $response; }
Аз съм много мързелив разработчик, затова добавих незадължителни параметри, които ми позволиха да декодирам отговори, да извлека данни и да проверя кодовете за отговор. Това доведе до друг метод, който включва заглавки по подразбиране, маркери за оторизация и някои допълнителни функции.
public function request($method, $path, $data = [], $autoCheckStatus = true, $autoDecode = true, $files = []) { if (!is_array($data)){ $data = array(); } $servers = ['Content-Type' => 'application/json']; if ($this->_token){ $servers['authorization'] = 'Token ' . $this->_token; } $this->_response = $this->browserRequest($method, $path, $data, $servers, $files); if ($autoCheckStatus === true){ $this->assertTrue($this->_response->isOk()); } elseif(ctype_alnum($autoCheckStatus)){ $this->assertEquals($autoCheckStatus, $this->_response->getStatusCode()); } if ($autoDecode){ $dataObject = json_decode($this->_response->getContent()); if (is_object($dataObject)) { // we have object at response return $this->_dataKeyAtResponse && property_exists($dataObject, $this->_dataKeyAtResponse) ? $dataObject->{$this->_dataKeyAtResponse} : $dataObject; } elseif (is_array($dataObject)) { // perhaps we have a collection return $this->_dataKeyAtResponse && array_key_exists($this->_dataKeyAtResponse, $dataObject) ? $dataObject[$this->_dataKeyAtResponse] : $dataObject; } else { // Uknown result return $dataObject; } } else { return $this->_response->getContent(); } }
Отново моите мързеливи навици ме накараха да добавя обвивки, предназначени да тестват CRUD страници:
create($data = [], $autoCheckStatus = true, $autoDecode = true, $files = []) show($id, $data = [], $autoCheckStatus = true, $autoDecode = true) update($id, $data = [], $autoCheckStatus = true, $autoDecode = true, $files = []) delete($id, $data = [], $autoCheckStatus = 204, $autoDecode = false) index($data = [], $autoCheckStatus = true, $autoDecode = true)
Един прост тестов код ще изглежда така:
// send wrong request // Validation error has code 422 in out specs $response = $this->create(['title'=>'', 'intro'=>'Ha-ha-ha. We have validators'], 422); // Try to create correct webshop $dataObject = $this->create( $data = [ 'title'=>'Super webshop', 'intro'=>'I am webshop', ] ); $this->assertGreaterThan(0, $dataObject->id); $this->assertData($data, $dataObject); // Try to request existing resource with method GET $checkData = $this->show($dataObject->id); // assertData is also a help method for validation objects with nested structure (not a part of PHPUnit). $this->assertData($data, $checkData); // Try to update with not valid data $this->update($dataObject->id, ['title'=> $data['title'] = ''], 422); // Try to update only title. Description should have old value. Title - changed $this->update($dataObject->id, ['title'=> $data['title'] = 'Super-Super SHOP!!!']); // Then check result with reading resource $checkData = $this->show($dataObject->id); $this->assertData($data, $checkData); // Read all created resources $list = $this->index(); $this->assertCount(1, $list); // TODO:: add checking for each item in the collection // Delete resoure $this->delete($dataObject->id); $this->show($dataObject->id, [], 404);
Ето още една полезна функция за проверка на данните за отговора, включително релации и вложени данни:
public function assertData($input, $checkData){ // it could be stdClass object after decoding json response $checkData = (array)$checkData; foreach($input as $k=>$v){ if (is_array($v) && (is_object($checkData[$k]) || is_array($checkData[$k]))) { // checking nested data recursively only if it exists in both: response data($input) and expected($checkData) $this->assertData($v, $checkData[$k]); } else { $this->assertEquals($v, $checkData[$k]); } } }
Дълго изпълняваните задачи са често срещано затруднение в уеб приложенията. Един прост пример би бил създаването на PDF отчет и неговото разпространение в организацията, което може да отнеме много време.
Блокирането на потребителски заявки за извършване на това действие не е желателен начин за справяне с проблема. Използване на Queue
в Laravel е чудесен начин за обработка и поставяне в опашка на продължителни задачи във фонов режим.
Един от последните проблеми беше генерирането на PDF и изпращането на отчети по имейл. Използвах драйвер за опашка по подразбиране за Beanstalkd, поддържан с пакета pda / pheanstalk. Много е лесно да добавите задачи към опашката
Например генерирането и изпращането на PDF файлове може да се направи по следния начин:
Queue::push('FlexiCallQueuePdfSend', [....data for sending to job's handler..], 'default');
Нашите PdfSend
манипулатор може да бъде реализиран по този начин:
class PdfSend extends BaseQueue{ function fire($job, $data){ //............................. //..... OUR IMPLEMENTATION..... //............................. } }
Спецификациите на Laravel предлагат проследяване на опашка с --daemon
опции, но използвам демона на надзора, който го поддържа постоянно да работи:
php artisan queue:listen --env=YOUR_ENV
Опашките са чудесни за задачи като запазване на данни. Те могат да се използват за повтаряне на неуспешни задачи, добавяне на тайм-аути за заспиване преди изпълнение на задания и т.н.
Споменах няколко ключови момента, свързани с Развитие на Laravel в този преглед на Laravel. Начинът, по който съберете всичко и изградите приложението си, ще зависи от вашата конкретна настройка и проект. Ето обаче кратък списък със стъпки, които може да искате да обмислите:
Моят филтър по подразбиране „преди“ създава глобален обхват като единичен и стартира таймера за наблюдение на производителността:
class AppBefore { function filter($request) { App::singleton('scope', function() { $scope = new AppScope();//My scope implementation $scope->startTimer(); return $scope; }); } }
Филтърът „след“ спира таймера, регистрира го в дневника за ефективност и изпраща заглавки CORS.
пружинно удостоверяване, базирано на токен за сигурност
class AppAfter{ function filter($request, $response){ $response->header('Access-Control-Allow-Origin', '*'); $response->header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); $response->header('Access-Control-Allow-Headers', 'Content-Type'); $response->header('Access-Control-Max-Age', '86400'); App::make('scope')->endTimer(); App::make('scope')->logTotalTime(); return $response; } }
Разработването на всеки от модулите ви вероятно ще изисква следните стъпки:
Настройте миграция на данни и семена по подразбиране. Внедряване на модели и връзки, трансформатори, валидатори и дезинфектанти (ако е необходимо). Напишете модулни тестове. Внедрете контролери и изпълнете тестове.
Използвам Laravel повече от година. Както при всяка технология, имаше някои проблеми с никненето на зъби, но вярвам, че изучаването на Laravel ме убеди, че това е невероятна рамка. Има още няколко неща, които не съм споменал в този урок на Laravel, но ако се интересувате да научите повече, може да помислите за тези точки и характеристики:
Успех и следете, Laravel е обещаваща рамка и вярвам, че ще съществува още години напред.
Свързани: Пълно удостоверяване на потребителя и контрол на достъпа - Урок за паспорт на Laravel, Pt. 1