- Introduction to the
- Preparation before development
- For the project
- Version of the relevant
- Environment set up
- Create a project
- API login
- Unified Restful API response processing
- Process the interface return value
- There are three ways to implement enumeration
- A single enumerated directory (one to one)
- Configuration file (one-to-many)
- Nested model (one to one)
- Interface screen
- Other configuration
- Language pack Installation
- Debug Debugging toolbar
- laravel-ide-helper
- Resolve cross-domain problems
- conclusion
Introduction to the
I have encountered such a problem before. I usually maintain the existing projects or add some new functions. Occasionally, when I need to take charge of a project independently, I feel a little “at a loss”. You don’t know how to put the pieces together, and when you’re done, you’re like, “Gee, why didn’t I do that? I wasn’t thinking about it, I should have done it, etc.”
So, I thought, why not make a demo project that I can use or others can use? Because the requirements are different, this demo project encapsulates only some general and commonly used content. If there is something wrong, please click the correct button at the bottom right, and we will improve it together.
Preparation before development
Scope of Application
Suitable for small and medium-sized projects, not involving high concurrency, etc., just as a beginner to use.
The project focuses on the API interface part.
Version of the relevant
Environment set up
By default, you have installed the required LNMP environment, please see Laravel development environment deployment for environment setup
If you prefer an integrated environment, Laragon, PHPStudy are recommended
Create a project
Through the Laravel installer
composer global require laravel/installer
Copy the code
Be sure to place Composer’s system-wide Vendor directory in your system environment variable $PATH. If ok, you can create a new project using the Laravel New project name.
Through the composer
Composer create-project --prefer-dist Laravel/Laravel project nameCopy the code
Pull projects from Github
You can pull this item directly by using the following command:
git clone https://github.com/Jouzeyu/api-demo.git
Copy the code
The related configuration
You can configure your database connection in the. Env file and then run the PHP artisan Migrate command to migrate the database.
API login
instructions
We use the Passport OAuth authentication as recommended, but you can also use the Dingo API. If your project is only used by a few people and you don’t need to refresh the token, you can also try Laravel API authentication.
The installation
composer require laravel/passport
Copy the code
Run the migration
php artisan migrate
Copy the code
After the migration, related tables will be automatically created in the database. If an error is displayed, please check whether the database connection in your. Env file is correct first.
Generate the secret key and client
Next, run the passport: Install command to create the encryption key needed to generate the secure access token, which also creates the “personal access” and “password authorization” clients used to generate the access token:
php artisan passport:install
Copy the code
Configuration and Use
Step 1: Reference HasApiTokens in the User model:
<? php namespace App; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use Notifiable, HasApiTokens; }Copy the code
In the second step: in the app/will/AuthServiceProvider boot method invokes the Passport: : routes.
<? php namespace App\Providers; use Laravel\Passport\Passport; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * The policy mappingsfor the application.
*
* @var array
*/
protected $policies = [
//'App\Model'= >'App\Policies\ModelPolicy'
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies(); Passport::routes(); }}Copy the code
Final step: In the config/auth.php configuration file, replace the API driver option with passport:
'guards'= > ['web'= > ['driver'= >'session'.'provider'= >'users',].'api'= > ['driver'= >'passport'.'provider'= >'users',]],Copy the code
Recommended article: Use Laravel Passport processing API authentication
Project related: Complete API login (other parts)
1. Create controller.php under app/Http/Controllers/Api and say:
<? php namespace App\Http\Controllers\Api; use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController { }Copy the code
2. Create the Auth controller and modify it as follows:
<? php namespace App\Http\Controllers\Api; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Carbon\Carbon; use App\User; use Illuminate\Support\Facades\Hash; class AuthController extends Controller { /** * Create user * * @param [string] name * @param [string] email * @param [string] password * @return [string] message
*/
public function register(Request $request)
{
$request->validate([
'name'= >'required|string'.'email'= >'required|string|email|unique:users'.'password'= >'required|string'
]);
$user = new User([
'name'= >$request->name,
'email'= >$request->email,
'password' => Hash::make($request->password)
]);
$user->save();
return response()->json([
'message'= >'User created successfully'
], 201);
}
/**
* Login user and create token
*
* @param [string] email
* @param [string] password
* @param [boolean] remember_me
* @return [string] access_token
* @return [string] token_type
* @return [string] expires_at
*/
public function login(Request $request)
{
$request->validate([
'email'= >'required|string|email'.'password'= >'required|string'.'remember_me'= >'boolean'
]);
$credentials = request(['email'.'password']);
if(! Auth::attempt($credentials))
return response()->json([
'message'= >'Wrong username or password'
], 401);
$user = $request->user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
if ($request->remember_me)
$token->expires_at = Carbon::now()->addWeeks(1);
$token->save();
return response()->json([
'access_token'= >$tokenResult->accessToken,
'token_type'= >'Bearer'.'expires_at' => Carbon::parse(
$tokenResult->token->expires_at
)->toDateTimeString()
]);
}
/**
* Get the authenticated User
*
* @return [json] user object
*/
public function userInfo(Request $request)
{
return response()->json($request->user()); }}Copy the code
3. API routing:
Route::group([
'prefix'= >'v1'].function () {
Route::post('login'.'Api\AuthController@login');
Route::post('register'.'Api\AuthController@register');
Route::group([
'middleware'= >'auth:api'].function() {
Route::get('user_info'.'Api\AuthController@userInfo');
});
});
Copy the code
Project related: test results
Registration Result:
Login Result:
Replacement details result:
Unified Restful API response processing
instructions
I had a private chat with the front end of the company about what kind of Restful API response would be more friendly for them to call. The front end said that it would be good if only unified. However, after trying and docking, I found that the back end capturing all exceptions and adding them to the returned data was more suitable for the front end separation project. So that’s what we’re doing here.
Encapsulate the unified return information
We’ll create a new Helpers folder in our app/Api folder and create a file called apiResponse.php to store the content we want to encapsulate:
<? php namespace App\Api\Helpers; use Symfony\Component\HttpFoundation\Response as FoundationResponse; use Response; trait ApiResponse { /** * @var int */ protected$statusCode= FoundationResponse::HTTP_OK; / * * * @return mixed
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @param $statusCode
* @return $this
*/
public function setStatusCode($statusCode.$httpCode=null)
{
$httpCode = $httpCode ?? $statusCode;
$this->statusCode = $statusCode;
return $this;
}
/**
* @param $data
* @param array $header
* @return mixed
*/
public function respond($data.$header = [])
{
return Response::json($data.$this->getStatusCode(),$header);
}
/**
* @param $status
* @param array $data
* @param null $code
* @return mixed
*/
public function status($status, array $data.$code = null){
if ($code) {$this->setStatusCode($code);
}
$status = [
'status'= >$status.'code'= >$this->statusCode
];
$data = array_merge($status.$data);
return $this->respond($data);
}
/**
* @param $message
* @param int $code
* @param string $status
* @returnMixed */ /* * format * data: * code:422 * message: XXX * status:'error'
*/
public function error($message.$code = FoundationResponse::HTTP_BAD_REQUEST,$status = 'error') {return $this->setStatusCode($code)->message($message.$status);
}
/**
* @param $message
* @param string $status
* @return mixed
*/
public function message($message.$status = "success") {return $this->status($status['message'= >$message
]);
}
/**
* @param string $message
* @return mixed
*/
public function internalError($message = "Internal Error!") {return $this->error($message,FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* @param string $message
* @return mixed
*/
public function created($message = "created")
{
return $this->setStatusCode(FoundationResponse::HTTP_CREATED)
->message($message);
}
/**
* @param $data
* @param string $status
* @return mixed
*/
public function success($data.$status = "success") {return $this->status($status,compact('data'));
}
/**
* @param string $message
* @return mixed
*/
public function notFond($message = 'Not Fond! ')
{
return $this->error($message,Foundationresponse::HTTP_NOT_FOUND); }}Copy the code
How to use
Modify the base class
<? php namespace App\Http\Controllers\Api; use App\Api\Helpers\ApiResponse; use App\Http\Controllers\Controller as BaseController; class Controller extends BaseController { use ApiResponse; // Reference the encapsulated error template}Copy the code
Other controller calls
1. Return the correct resource information
return $this->success($users);
Copy the code
2. The correct information about the customized HTTP status code is displayed
return $this->setStatusCode(201)->success($users);
Copy the code
3. An error message is displayed
return $this->error('User registration failed');
Copy the code
5. Error information about the user-defined HTTP status code is displayed
return $this->error('User login failed', 401);Copy the code
Disclaimer: This section is a manual guide to making Laravel API development more handy
Project related: improve other parts
1. Create a Users controller to facilitate testing:
<? php namespace App\Http\Controllers\Api; use App\User; use Illuminate\Http\Request; class UsersController extends Controller { publicfunction test(Request $request) {$user=User::where('id',1)->first();
return $this->success($user); }}Copy the code
2. Register a route
Route::group([
'prefix'= >'v1'].function () {
Route::get('test'.'Api\UsersController@test'); // No login authentication is required... });Copy the code
Project related: test results
Process the interface return value
Problems thrown
We limit the return value for security reasons and so on. As you can see, when User information is returned in the previous section, all data is returned, including sensitive information password, common sensitive information also includes wechat OpenID, etc. How to avoid this section is the main content.
API resources
Introduction to the
You often need a transformation layer to connect your Eloquent model to the ACTUAL JSON response that is returned to the user, known as the ViewModel layer.
Creating user Resources
php artisan make:resource Api/UserResource
Copy the code
PHP: userResources.php: userresources.php: userresources.php: userresources.php: userresources.php: userresources.php: userresources.php
<? php namespace App\Http\Resources\Api; use Illuminate\Http\Resources\Json\JsonResource; Class UserResource extends JsonResource {/** * this specifies the return value ** @param \Illuminate\Http\Request$request
* @return array
*/
public function toArray($request)
{
return [
'id'= >$this->id,
'name'= >$this->name,
'email'= >$this->email,
'created_at'=>(string)$this->created_at,
'updated_at'=>(string)$this->updated_at ]; }}Copy the code
How to use
To open our User test controller, we will:
return $this->success($user);
Copy the code
Replace with:
return $this->success(new UserResource($user));
Copy the code
Note: This method is only suitable for returning a single user, otherwise paging will not work. For a list of users, you can use return UserResource:: Collection ($user);
Project related: test results
There are three ways to implement enumeration
Now there is a requirement to add state to the user, such as freeze, normal. This is a very common requirement, we usually add a status field in the data table, and use a number to represent identity, but we know what the number means for a short time, and then we forget it, and the front end people don’t know. So what do we do? We use enumerations.
A separate enumerated directory
Creating an Enum directory
Create an Enum directory under app to store our enumeration files, such as userenum.php:
<? php namespace App\Enum; Class UserEnum {// State class const NORMAL = 1; // Normal const FREEZE = 2; // Freeze public staticfunction getStatusName($status){
switch ($status) {case self::NORMAL:
return 'normal';
case self::FREEZE:
return 'freeze';
default:
return 'normal'; }}}Copy the code
use
Modify UserResource. PHP:
return [
'id'= >$this->id,
'name'= >$this->name,
'email'= >$this->email,
'status' => UserEnum::getStatusName($this->status),
'created_at'=>(string)$this->created_at,
'updated_at'=>(string)$this->updated_at
];
Copy the code
Tip: frozen state should not be able to log in, here the judgment logic please write their own
The configuration file
Because it’s a little bit longer, you can look at my previous blog and see how to gracefully use helpers.php, right
Nested too in the model
In the same way, if you look at my previous blog, you can protect the type field and so on.
Enumeration is not included in the demo project because everyone likes it differently.
Interface screen
instructions
We are almost there, but we still need one important module, which is interface filtering. The goal is to get the corresponding filtered data through different parameters in the front end. We use the Laravel-Query-Builder extension package here.
The installation
composer require spatie/laravel-query-builder
Copy the code
Release configuration
php artisan vendor:publish --provider="Spatie\QueryBuilder\QueryBuilderServiceProvider" --tag="config"
Copy the code
How to use
1. First make sure the required fields are allowed in the model, for example in the User model:
protected $fillable = [
'name'.'email'.'password',];Copy the code
2. Used in the controller:
<? php namespace App\Http\Controllers\Api; use App\Http\Resources\Api\UserResource; use App\User; use Illuminate\Http\Request; use Spatie\QueryBuilder\QueryBuilder; class UsersController extends Controller { publicfunction test(Request $request) {$users = QueryBuilder::for(User::class)
->allowedFilters(['name'])
->get();
return UserResource::collection($users); }}Copy the code
3. Front-end screening
http://api-demo.test/api/v1/test? filter[name]=jouzeyuCopy the code
Use Spatie\QueryBuilder\QueryBuilder; And not the other way around. We use allowedFilters to declare fields that are allowed to be filtered. This is just a preliminary introduction, please see the documentation for more information
Other configuration
Language pack Installation
Composer require caouecs/laravel - lang: ~ 4.0Copy the code
Debug Debugging toolbar
composer require barryvdh/laravel-debugbar --dev
Copy the code
Tip: –dev means install locally only
laravel-ide-helper
composer require --dev barryvdh/laravel-ide-helper
Copy the code
Resolve cross-domain problems
The installation
composer require barryvdh/laravel-cors
Copy the code
configuration
Add to app/Http/ kernel.php:
protected $middleware = [
// ...
\Barryvdh\Cors\HandleCors::class,
];
Copy the code
conclusion
The demo project is over for the time being, maybe he is not perfect, but I still hope to inspire friends who have just started. If you have a better idea, feel free to comment below. Thanks.
Open source: github.com/Jouzeyu/api…