2014-04-16 39 views
27

我想在Laravel中创建一个API第一个应用程序。我不知道最佳做法是什么,我会解释我正在尝试做什么,但请随时以不同的方式回答如何做到这一点。如何在Laravel中创建REST API第一个Web应用程序

我不希望我所有的前端都用javascript编写,并用angular.js或类似的东西解析API的JSON输出。我想让我的Laravel应用程序生成HTML视图。我正试图走上两个控制器,一个用于API,另一个用于Web。为展示用户操作我的routes.php文件是这样的:

# the web controller 
Route::controller('user', 'WebUserController'); 

# the api controller 
Route::group(array('prefix' => 'api'), function() { 
    Route::resource('user', 'UserController'); 
}); 

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

WebUserController我能够获得与getData()响应的JSON的内容,但我不能够获得头和状态码(保护它们的属性的Illuminate\Http\JsonResponse)。

我认为我的方法可能不是最好的,所以我接受建议如何使这个应用程序。

编辑:现在的问题是如何让头和响应的状态已经被Drew Lewis回答,但我仍然认为,有可能是一个更好的方法如何设计这个

+0

马丁你好,我有同样的问题要在Laravel 5.1中解决。那么,你是如何实施的?你有没有使用Repositor模式? – Ashish

+0

@Ashish,当我问这个时,我和Nyan的答案一起去了。这似乎是最简单的解决方案,并做了我所需要的。虽然我还没有与Laraval 5.1合作,但不知道自那以后发生了什么变化。 –

+0

您是否为Web和API创建单独的控制器,如果是的话,您是如何设法防止代码重复的,我想知道存储库设计模式,我们只能将DB逻辑移出控制器 – Ashish

回答

38

你应该利用库/网关设计模式:请参阅答案here

例如,在处理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) 

} 

然后,创建一个服务提供商,结合您的用户库界面,您的口才用户库。无论何时您需要用户资源库(通过IoC容器解析它或在构造函数中注入依赖项),Laravel都会自动为您提供刚创建的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 
     ); 
    } 

} 

接下来,创建一个用户网关,谁的目的是与任意数量的存储库交谈并执行应用程序的任何业务逻辑:

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

} 

最后,创建您的用户Web控制器。该控制器会谈到你的用户网关:

class UserController extends BaseController 
{ 
    public function __construct(UserGatway $userGateway) 
    { 
     $this->userGateway = $userGateway; 
    } 

    public function create() 
    { 
     $user = $this->userGateway->createUser(Input::all()); 

    } 
} 

通过构建这样的应用程序的设计,你会得到几个好处:你获得的关注非常清晰的分离,因为你的应用程序将秉承Single Responsibility Principle(通过将您的业务逻辑从数据库逻辑中分离出来)。这使您能够以更简单的方式执行单元和集成测试,使您的控制器尽可能瘦,并且如果您将来愿意,可以轻松更换任何其他数据库的Eloquent。

例如,如果从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 
    } 

    ... 

} 

和服务提供商现在的UserRepository接口绑定到新的MongoUserRepository:

$this->app->bind(
     'lib\Repositories\UserRepository',  
     'lib\Repositories\MongoUserRepository' 
); 

纵观所有网关你一直在引用UserRepository,所以通过做出这个改变,你实际上告诉Laravel使用新的MongoUserRepository而不是旧的Eloquent。不需要其他更改。

+1

谢谢,这是一个比Nyan的答案更复杂的设计......你能解释一下什么是好处。例如,如果要更改另一个ORM的Eloquent,则需要更改EloquentUserRepository,但也需要更改所有网关。 –

+0

请重新阅读我的答案 - 我添加了更多信息。如果更改为另一个ORM,则不需要更改任何网关,只需更改服务提供商和存储库即可。 – seeARMS

+5

太多的锅炉板,只是为了不太可能改变ORM的情况。 – malhal

1

我要你与应对所遇到的问题的回应。 您可以从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(); 

$响应 - >头是Symfony的\分量\ HttpFoundation \ ResponseHeaderBag从Symfony的\组件将继承\ HttpFoundation \ HeaderBag

+0

耶,提取所有我需要的东西。一般情况下怎么样......你能想出一个更好的主意吗? –

+0

@马丁im想法的方式,只有一个控制器的两个。为响应构建数据并将其传递给基于它是否来自api路由的中间方将返回正确的响应。但它取决于你期望Api @ show与Web @ getView在数据方面有多不同。 – lagbox

7

你应该使用存储库这样的设计。

示例 -

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

这看起来很好,很简单,每个型号只有一个额外的类。 –

2

这是Jeffrey Way的视频,他是Laravel更好的开发者之一。在本教程中,他将BackboneJS应用程序连接到他在Laravel中设置的RESTful服务。这没有比这更好。我可以给你写很多样板,但只需通过观看漂亮的视频和喝咖啡来学习。 ;)

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

+0

嗯,这个似乎展示了如何在laravel中创建一个后端json api,并且在骨干中有一个前端,这并不是我正在寻找的。 –

+0

只需使用json_decode(),你有一个php数组,并忽略它的BackboneJS。它是RESTful的,所以调用API无关紧要。它返回JSON的事实很棒,因为任何语言都可以处理JSON! – sidneydobber

+0

我没有看到让Laravel将PHP数组转换为JSON字符串,然后在您自己的代码中手动解码它。 –

1

我也建议使用一个仓库。 试图从另一个控制器调用另一个控制器会落入称为HMVC(分层模型 - 视图 - 控制器)的模式。 这意味着您的整个应用程序依赖于较低的模块。 在这种情况下,您的API将作为您的数据存储库(这不是世界上最糟糕的事情)。

但是,当您修改API中的数据返回结构时,其他所有依赖于它的结构都必须知道如何响应。 假设你想进行授权检查,看看登录的用户是否应该能够看到返回用户的详细信息,并且出现错误。

在API中,您将返回一个包含403禁止代码和一些元数据的Response对象。 您的HTML控制器必须知道如何处理这个问题。

将此对比于可能引发异常的存储库。

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

而且你的API控制器看起来更像是这样的:那么

public function show($id) 
{ 
    try { 
     return $this->user->findById($id); 
    } catch (UnauthorizedAccessException $e) { 
     $message = $e->getMessage(); 
     return Response::json('403', ['meta' => ['message' => $message]])); 
    } 
} 

你的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 post中实现存储库模式的更多信息:如果您愿意,可以忽略该接口并跳过实现。

相关问题