我的应用程序是一个图书管理器,我可以在其中创建图书和页面。
我的bookController在POST上有一个"存储",它存储标题和描述。
public function store()
{
$rules = array(
'title' => 'required|min:3',
'description' => 'required|min:30'
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Response::json(
array(
'metadata' => array(
'error' => true,
'message' => 'The book creation has failed'
)
),
400
);
}
else {
$slug = Str::slug(Request::get('title'));
$existSlug = Book::where('slug',$slug)->get();
if(count($existSlug) > 0) {
return Response::json(
array(
'metadata' => array(
'error' => true,
'message' => 'This title is already taken'
)
),
400
);
}
else {
$book = new Book;
$book->title = Request::get('title');
$book->slug = $slug;
$book->description = Request::get('description');
$book->user_id = Auth::user()->id;
$book->status = false;
$book->save();
$stored = $book->toArray();
$metadata = array(
'metadata' => array(
'error' => false,
)
);
return Response::json(
array_merge($stored,$metadata),
201
);
}
}
}
我还有一个pageController,在POST上有一个"存储",它存储页面内容:
public function store()
{
$rules = array(
'content' => 'required|between:300,350',
'book_id' => 'required|exists:books,id'
);
$validator = Validator::make(Input::all(), $rules);
if($validator->fails()) {
return Response::json(
array(
'metadata' => array(
'error' => true,
'message' => 'The page must be between 300 and 350 characters'
)
),
400
);
}
else {
$book = Book::find(Input::get('book_id'));
$content = Input::get('content');
$parent = Page::where('book_id',$book->id)->where('status',1)->orderBy('id', 'desc')->first();
if($parent){
$parent_id = $parent->id;
$parent_number = $parent->number;
$status = 0; //Define the status of the created page
}
else{
//If it's the first page of the book
$parent_id = 0;
$parent_number = 0;
$status = 1; //if there's no parent page, the new page is the first - auto validated - page of the book.
if($book->user_id != Auth::user()->id) {
return Response::json(
array(
'metadata' => array(
'error' => true,
'message' => 'You have to be the author of a book to write the first page.'
)
),
403
);
}
}
$page = new Page;
$page->content = $content;
$page->book_id = $book->id;
$page->parent_id = $parent_id;
$page->number = $parent_number + 1;
$page->user_id = Auth::user()->id;
$page->status = $status;
$page->save();
$stored = $page->toArray();
$metadata = array(
'metadata' => array(
'error' => false
)
);
return Response::json(
array_merge($stored,$metadata),
201
);
}
}
每当有人创作一本书时,他至少要写第一页。这将产生一个带有输入标题、描述和内容的表单。
我向[…]/books发送了一个POST,其中包含我输入的标题和描述
如果成功=>我获得图书id,并将其与输入内容一起发送到[…]/pages。
以下是我的问题:
- 有人可以在[…]/books上发帖,并将存储一本没有页面的新书
- 我想用更"restFUL的方式"来解决这个问题,这意味着没有像将内容发送到/books并在bookController中进行页面验证这样的"黑客解决方案"
- 此外,即使我选择了破解的方式,我的API仍然不安全:我可以停止要发送的第二个请求(到/页面)
如何处理这种共同依赖关系?
1st
你的控制器做得太多了,他们不应该知道你的业务逻辑——这是应该由特定类(模型、存储库、域逻辑类)处理的事情。
创建一些类来处理这个逻辑,将Input发送给它们并实现它。使用Laravel可以随心所欲地调用它们,因为你可以用代码做任何你想做的事情。
第二
如果您有不同的数据约束需要强制执行,您可以:
根据相同的请求处理它们
根据您的界面,如果您在一个页面上拥有所需的一切,您只需发送数据并在存储库中处理即可,存储库可以访问您的所有模型。
一个可以用于两者的例子是:
使用依赖注入的图书存储库,这意味着book和Page将由Laravel:自动实例化
class BookRepository {
__construct(Book $book, Page $page)
{
$this->book = $book;
$this->page = $page;
}
public function store($input)
{
if ( ! $this->book->validate($input) || !$this->page->validate($input))
{
return 'error';
}
$book->create(input);
$page->create($input);
}
}
基础模型与您的验证:
class Book extends BaseModel {
public function validate($input)
{
/// validate here and return
}
}
您的模型和规则:
class Book extends BaseModel {
$book_rules = array(
'title' => 'required|min:3',
'description' => 'required|min:30'
);
}
class Page extends BaseModel {
$page_rules = array(
'content' => 'required|between:300,350',
'book_id' => 'required|exists:books,id'
);
}
然后创建一个包含图书信息和页面信息的视图,该视图将POST到BookController@store:
class BookController extends Controller {
public function __controller(BookRepository $book_repository)
{
$this->book_repository = $book_repository;
}
public function store()
{
if ( ! $this->book_repository->store($input))
{
return Redirect::back()
->withErrors(
$this->book_repository
->validation
->messages()
->all()
);
}
return Redirect::to('success');
}
}
我们再次使用依赖注入$book_repository将自动实例化。因此,您的Controller不需要知道Book或Page的功能,只需要获取请求并将其传递给负责处理所有内容的存储库即可。
这还不是全部,但这只是一个开始。
处理不同的请求
这是常有的事。用户发送请求,应用程序检查并存储数据。用户发送第二个请求,应用程序检查所有内容,并在需要时发回错误。
在后台处理它们
这是一种更聪明的方法。你的应用程序将在一个或多个请求中接收所有数据,存储它们,使用队列工作者进行检查,并向用户发送电子邮件,告诉他有一些数据需要填写。没有页面的书可以在一段时间后删除。你不会冒数据不好的风险,一旦你这样做了,你的用户就会知道缺少了什么。