php 如何在 Laravel 中制作 REST API 第一个 Web 应用程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23115291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make a REST API first web application in Laravel
提问by Martin Taleski
I want to make an API first application in Laravel. I don't know what is the best approach to do this, I will explain what I am trying to do, but please feel free to give answers how to do this in a different way.
我想在 Laravel 中创建一个 API 第一个应用程序。我不知道这样做的最佳方法是什么,我将解释我想要做什么,但请随时给出如何以不同方式做到这一点的答案。
I don't want all my frontend to be written in javascript and parse the JSON output of the API with angular.js or something similar. I want my Laravel application to produce the HTML views. I am trying to go down the road of having two controllers one on for the API and one for the web. For the show User action my routes.php looks like this:
我不希望我的所有前端都用 javascript 编写并使用 angular.js 或类似的东西解析 API 的 JSON 输出。我希望我的 Laravel 应用程序生成 HTML 视图。我正试图走上拥有两个控制器的道路,一个用于 API,一个用于 Web。对于 show User 操作,我的 routes.php 如下所示:
# the web controller
Route::controller('user', 'WebUserController');
# the api controller
Route::group(array('prefix' => 'api'), function() {
Route::resource('user', 'UserController');
});
So /user
will take me to WebUserController
and /api/user
will take me to the UserController
. Now I want to put all my logic in the API UserController
, and call its actions from the WebUserController
. Here is the code for both of them:
所以/user
会带我去WebUserController
,/api/user
也会带我去UserController
。现在我想把我所有的逻辑放在 API 中UserController
,并从WebUserController
. 这是他们两个的代码:
class UserController extends BaseController
{
public function show($id)
{
$user = User::find($id);
return Response::json(array('success'=>true,'user'=>$user->toArray()));
}
}
class WebUserController extends UserController
{
public function getView($id)
{
# call the show method of the API's User Controller
$response = $this->show($id);
return View::make('user.view')->with('data', $response->getData());
}
}
In the WebUserController
I am able to get the json content of the response with getData()
, but I am not able to get the headers and status code (they are protected properties of Illuminate\Http\JsonResponse
).
在 中,WebUserController
我可以使用 获取响应的 json 内容getData()
,但无法获取标头和状态代码(它们是 的受保护属性Illuminate\Http\JsonResponse
)。
I think that my approach might not be the best, so I am open to suggestions how to make this app.
我认为我的方法可能不是最好的,所以我愿意就如何制作这个应用程序提出建议。
EDIT: The question how to get the headers and status of the response has been answered by Drew Lewis, but I still think that there might be a better way how to design this
编辑:Drew Lewis已经回答了如何获取响应的标题和状态的问题,但我仍然认为可能有更好的方法来设计它
回答by seeARMS
You should utilize the Repository / Gateway design pattern: please see the answers here.
您应该使用存储库/网关设计模式:请在此处查看答案。
For example, when dealing with the User model, first create a User Repository. The onlyresponsibility of the user repository is to communicate with the database (performing CRUD operations). This User Repository extends a common base repository and implements an interface containing all methods you require:
例如,在处理 User 模型时,首先创建一个 User Repository。用户存储库的唯一职责是与数据库通信(执行 CRUD 操作)。此用户存储库扩展了一个公共基础存储库并实现了一个包含您需要的所有方法的接口:
class EloquentUserRepository extends BaseRepository implements UserRepository
{
public function __construct(User $user) {
$this->user = $user;
}
public function all() {
return $this->user->all();
}
public function get($id){}
public function create(array $data){}
public function update(array $data){}
public function delete($id){}
// Any other methods you need go here (getRecent, deleteWhere, etc)
}
Then, create a service provider, which binds your user repository interface to your eloquent user repository. Whenever you require the user repository (by resolving it through the IoC container or injecting the dependency in the constructor), Laravel automatically gives you an instance of the Eloquent user repository you just created. This is so that, if you change ORMs to something other than eloquent, you can simply change this service provider and no other changes to your codebase are required:
然后,创建一个服务提供者,它将您的用户存储库界面绑定到您的 eloquent 用户存储库。每当您需要用户存储库时(通过 IoC 容器解析它或在构造函数中注入依赖项),Laravel 会自动为您提供您刚刚创建的 Eloquent 用户存储库的实例。这样一来,如果您将 ORM 更改为 eloquent 以外的其他内容,您只需更改此服务提供程序,而无需对代码库进行其他更改:
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind(
'lib\Repositories\UserRepository', // Assuming you used these
'lib\Repositories\EloquentUserRepository' // namespaces
);
}
}
Next, create a User Gateway, who's purpose is to talk to any number of repositories and perform any business logic of your application:
接下来,创建一个用户网关,其目的是与任意数量的存储库对话并执行应用程序的任何业务逻辑:
use lib\Repositories\UserRepository;
class UserGateway {
protected $userRepository;
public function __construct(UserRepository $userRepository) {
$this->userRepository = $userRepository;
}
public function createUser(array $input)
{
// perform any sort of validation first
return $this->userRepository->create($input);
}
}
Finally, create your User web controller. This controller talks to your User Gateway:
最后,创建您的用户 Web 控制器。此控制器与您的用户网关对话:
class UserController extends BaseController
{
public function __construct(UserGatway $userGateway)
{
$this->userGateway = $userGateway;
}
public function create()
{
$user = $this->userGateway->createUser(Input::all());
}
}
By structuring the design of your application in this way, you get several benefits: you achieve a very clear separation of concerns, since your application will be adhering to the Single Responsibility Principle(by separating your business logic from your database logic) . This enables you to perform unit and integration testing in a much easier manner, makes your controllers as slim as possible, as well as allowing you to easily swap out Eloquent for any other database if you desire in the future.
通过以这种方式构建您的应用程序的设计,您将获得几个好处:您实现了非常明确的关注点分离,因为您的应用程序将遵守单一职责原则(通过将您的业务逻辑与您的数据库逻辑分离)。这使您能够以更简单的方式执行单元和集成测试,使您的控制器尽可能地纤细,并允许您在将来需要时轻松地将 Eloquent 替换为任何其他数据库。
For example, if changing from Eloquent to Mongo, the only things you need to change are the service provider binding as well as creating a MongoUserRepository which implements the UserRepository interface. This is because the repository is the onlything talking to your database - it has no knowledge of anything else. Therefore, the new MongoUserRepository might look something like:
例如,如果从 Eloquent 更改为 Mongo,您唯一需要更改的是服务提供者绑定以及创建一个实现 UserRepository 接口的 MongoUserRepository。这是因为存储库是唯一与您的数据库对话的东西——它不知道其他任何东西。因此,新的 MongoUserRepository 可能类似于:
class MongoUserRepository extends BaseRepository implements UserRepository
{
public function __construct(MongoUser $user) {
$this->user = $user;
}
public function all() {
// Retrieve all users from the mongo db
}
...
}
And the service provider will now bind the UserRepository interface to the new MongoUserRepository:
服务提供者现在会将 UserRepository 接口绑定到新的 MongoUserRepository:
$this->app->bind(
'lib\Repositories\UserRepository',
'lib\Repositories\MongoUserRepository'
);
Throughout all your gateways you have been referencing the UserRepository, so by making this change you're essentially telling Laravel to use the new MongoUserRepository instead of the older Eloquent one. No other changes are required.
在您所有的网关中,您一直在引用 UserRepository,因此通过进行此更改,您实际上是在告诉 Laravel 使用新的 MongoUserRepository 而不是旧的 Eloquent 。不需要其他更改。
回答by Nyan Lynn Htut
You should be use Repository for this design.
您应该将 Repository 用于此设计。
Example -
例子 -
//UserRepository Class
class UserRepository {
public function getById($id)
{
return User::find($id);
}
}
// WebUser Controller
class WebUserController extends BaseController {
protected $user;
public function __construct(UserRepository $user)
{
$this->user = $user;
}
public function show($id)
{
return View::make('user.view')->with('data', $this->user->getById($id));
}
}
// APIUser Controller
class UserController extends BaseController {
protected $user;
public function __construct(UserRepository $user)
{
$this->user = $user;
}
public function show($id)
{
$data =>$this->user->getById($id);
return Response::json(array('success'=>true,'user'= $data->toArray()));
}
}
回答by Christopher Rathgeb
Checkout Laravel's RESTful controllers:
查看 Laravel 的 RESTful 控制器:
http://laravel.com/docs/controllers#restful-controllers
http://laravel.com/docs/controllers#restful-controllers
Their docs do a pretty good job.
他们的文档做得很好。
But even better is this tutorial:
但更好的是本教程:
http://code.tutsplus.com/tutorials/laravel-4-a-start-at-a-restful-api-updated--net-29785
http://code.tutsplus.com/tutorials/laravel-4-a-start-at-a-restful-api-updated--net-29785
回答by sidneydobber
This is a video by Jeffrey Way he is one of the better Laravel developers. In this tutorial he is connecting a BackboneJS application to a RESTful service that he sets up in Laravel. It doesn't get any better then this. I can write you a lot of boilerplate, but just learn it by watching a nice video and having a coffee. ;)
这是 Jeffrey Way 的视频,他是更好的 Laravel 开发人员之一。在本教程中,他将 BackboneJS 应用程序连接到他在 Laravel 中设置的 RESTful 服务。没有比这更好的了。我可以给你写很多样板,但只需通过观看一段精彩的视频和喝杯咖啡来学习。;)
回答by Ryan Tablada
I would also recommend using a repository. Attempting to call one controller from another would be falling into a pattern called HMVC (Hierarchical model–view–controller). This means that your entire application relies on lower modules. In this case, your API would serve as a repository for your data (which isn't the worst thing in the world at first).
我还建议使用存储库。尝试从另一个控制器调用一个控制器将陷入一种称为 HMVC(分层模型-视图-控制器)的模式。这意味着您的整个应用程序依赖于较低的模块。在这种情况下,您的 API 将充当您的数据的存储库(起初这并不是世界上最糟糕的事情)。
However, when you then modify the structure of how data is returned in your API, everything else relying on it would have to know how to respond. Say you wanted to have authorization checks to see if a logged in user should be able to see the details of a returned user and there was an error.
但是,当您随后修改 API 中数据返回方式的结构时,依赖它的所有其他内容都必须知道如何响应。假设您希望进行授权检查,以查看登录用户是否应该能够查看返回用户的详细信息,并且出现错误。
In the API, you would return a Response object with a 403 forbidden code and some meta data. Your HTML controller would have to know how to handle this.
在 API 中,您将返回一个带有 403 禁止代码和一些元数据的 Response 对象。您的 HTML 控制器必须知道如何处理这个问题。
Contrast this to a repository which could throw an exception.
将此与可能引发异常的存储库进行对比。
public function findById ($id)
{
$user = User::findOrFail($id);
if (Auth::user->hasAccessTo($user)) {
return $user;
} else {
throw new UnauthorizedAccessException('you do not have sufficient access to this resource');
}
}
And your API controller would look more like this:
你的 API 控制器看起来更像这样:
public function show($id)
{
try {
return $this->user->findById($id);
} catch (UnauthorizedAccessException $e) {
$message = $e->getMessage();
return Response::json('403', ['meta' => ['message' => $message]]));
}
}
Your HTML controller would then look like this:
您的 HTML 控制器将如下所示:
public function show($id)
{
try {
$user = $this->user->findById($id);
} catch (UnauthorizedAccessException $e) {
Session::flash('error', $e->getMessage());
// Redirect wherever you would like
return Response::redirect('/');
}
}
This gives you very reusable code and let's you change your controller implementations independently without worry of changing the other's behavior. I wrote more on how to implement the repository pattern in this post: you can ignore the interface and skip right to the implementations if you would like.
这为您提供了非常可重用的代码,让您可以独立更改控制器实现,而不必担心更改其他人的行为。我在这篇文章中写了更多关于如何实现存储库模式的内容:如果你愿意,你可以忽略接口并直接跳到实现。
回答by lagbox
I have a response to the problem you are having with the Response. You can get the headers, status code and data from the Response.
我对您在 Response 中遇到的问题有一个回应。您可以从响应中获取标头、状态代码和数据。
// your data
$response->getData();
// the status code of the Response
$response->getStatusCode();
// array of headers
$response->headers->all();
// array of headers with preserved case
$response->headers->allPreserveCase();
$response->headers is a Symfony\Component\HttpFoundation\ResponseHeaderBag which inherits from Symfony\Component\HttpFoundation\HeaderBag
$response->headers 是一个 Symfony\Component\HttpFoundation\ResponseHeaderBag,它继承自 Symfony\Component\HttpFoundation\HeaderBag