如何在Laravel中创建REST API的第一个web应用程序

时间:2022-03-05 20:04:58

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编写,并用角度解析API的JSON输出。js或类似的东西。我希望Laravel应用程序生成HTML视图。我试着沿着这条路走,有两个控制器,一个是API,一个是网络。对于show User action my route。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:

所以/用户会把我带到WebUserController, /api/用户会把我带到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中,我可以使用getData()获取响应的json内容,但是我不能获得报头和状态代码(它们是illumin\ 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的回答,但是我仍然认为可能有更好的方法来设计它

6 个解决方案

#1


40  

You should utilize the Repository / Gateway design pattern: please see the answers here.

您应该使用Repository / Gateway设计模式:请在这里看到答案。

For example, when dealing with the User model, first create a User Repository. The only responsibility 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:

例如,在处理用户模型时,首先创建一个用户存储库。用户存储库的唯一职责是与数据库通信(执行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:

然后,创建一个服务提供者,它将用户存储库接口绑定到有说服力的用户存储库。每当您需要用户存储库(通过通过IoC容器解决它或在构造函数中注入依赖项)时,Laravel会自动给您一个您刚刚创建的有意义的用户存储库的实例。因此,如果您将ORMs更改为非雄辩的内容,您只需更改此服务提供者,不需要对代码库进行任何其他更改:

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.

通过以这种方式构造应用程序的设计,您将获得一些好处:您可以实现非常清晰的关注点分离,因为您的应用程序将遵循单一职责原则(通过将业务逻辑与数据库逻辑分离)。这使您能够以一种更简单的方式执行单元和集成测试,使您的控制器尽可能地苗条,并且如果您希望将来使用任何其他数据库,您可以轻松地交换数据。

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 only thing talking to your database - it has no knowledge of anything else. Therefore, the new MongoUserRepository might look something like:

例如,如果从雄辩到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接口绑定到新的monsergourepository:

 $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,而不是老的有意义的。不需要其他更改。

#2


7  

You should be use Repository for this design.

您应该使用此设计的存储库。

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()));
    }
}

#3


2  

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 -开始- - - restful api更新-网- 29785

#4


2  

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服务。没有比这更好的了。我可以给你写很多样板文件,但是只要看个视频喝杯咖啡就可以了。,)

https://www.youtube.com/watch?v=uykzCfu1RiQ

https://www.youtube.com/watch?v=uykzCfu1RiQ

#5


1  

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.

我对你们的问题有一个反应。您可以从响应中获取报头、状态代码和数据。

// 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\组件\HttpFoundation\ResponseHeaderBag继承自Symfony\组件\HttpFoundation\HeaderBag

#6


1  

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禁止代码和一些元数据的响应对象。你的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.

这为您提供了非常可重用的代码,让我们独立地更改控制器实现,而不必担心更改另一个控制器的行为。我在这篇文章中写了更多关于如何实现存储库模式的内容:如果您愿意,可以忽略接口并直接跳到实现。

#1


40  

You should utilize the Repository / Gateway design pattern: please see the answers here.

您应该使用Repository / Gateway设计模式:请在这里看到答案。

For example, when dealing with the User model, first create a User Repository. The only responsibility 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:

例如,在处理用户模型时,首先创建一个用户存储库。用户存储库的唯一职责是与数据库通信(执行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:

然后,创建一个服务提供者,它将用户存储库接口绑定到有说服力的用户存储库。每当您需要用户存储库(通过通过IoC容器解决它或在构造函数中注入依赖项)时,Laravel会自动给您一个您刚刚创建的有意义的用户存储库的实例。因此,如果您将ORMs更改为非雄辩的内容,您只需更改此服务提供者,不需要对代码库进行任何其他更改:

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.

通过以这种方式构造应用程序的设计,您将获得一些好处:您可以实现非常清晰的关注点分离,因为您的应用程序将遵循单一职责原则(通过将业务逻辑与数据库逻辑分离)。这使您能够以一种更简单的方式执行单元和集成测试,使您的控制器尽可能地苗条,并且如果您希望将来使用任何其他数据库,您可以轻松地交换数据。

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 only thing talking to your database - it has no knowledge of anything else. Therefore, the new MongoUserRepository might look something like:

例如,如果从雄辩到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接口绑定到新的monsergourepository:

 $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,而不是老的有意义的。不需要其他更改。

#2


7  

You should be use Repository for this design.

您应该使用此设计的存储库。

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()));
    }
}

#3


2  

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 -开始- - - restful api更新-网- 29785

#4


2  

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服务。没有比这更好的了。我可以给你写很多样板文件,但是只要看个视频喝杯咖啡就可以了。,)

https://www.youtube.com/watch?v=uykzCfu1RiQ

https://www.youtube.com/watch?v=uykzCfu1RiQ

#5


1  

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.

我对你们的问题有一个反应。您可以从响应中获取报头、状态代码和数据。

// 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\组件\HttpFoundation\ResponseHeaderBag继承自Symfony\组件\HttpFoundation\HeaderBag

#6


1  

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禁止代码和一些元数据的响应对象。你的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.

这为您提供了非常可重用的代码,让我们独立地更改控制器实现,而不必担心更改另一个控制器的行为。我在这篇文章中写了更多关于如何实现存储库模式的内容:如果您愿意,可以忽略接口并直接跳到实现。