drm30963
2013-12-05 20:16
浏览 39
已采纳

restFUL API依赖于商店

my app is a Book manager where I can create Books and Pages.

I have my bookController with a "store" on POST, which store a title and a description.

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

    }

}

I also have a pageController with a "store" on POST, which store a page content :

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

Whenever someone creates a book, he has to write at least its first page. This result in a form with an input title, description and content.

I send a POST to [...]/books with my input title and description

If Success => I get the book id, and send it with the input content to [...]/pages.

Here are my problems :

  • Someone can send a post on [...]/books and will store a new book with no page
  • I want to solve this in the more "restFUL way", meaning no "hackish solution" like sending the content to /books and make a page validation in the bookController
  • Also, even if I chose the hackish way, my API is still not safe : I can stop the second request (to /pages) to be sent.

How do I handle this co-dependency ?

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • duangaoe9401 2013-12-05 20:41
    已采纳

    1st

    Your controllers are doing too much, they are not supposed to know anything about your business logic this is something that should be handle by specific classes (models, repositories, domain logic classes).

    Create some classes to handle this logic, send the Input to them and make it happen. Call them whatever you need to, using Laravel is great because you can do whatever you want with your code.

    2nd

    If you have different data constraints to be enforced, you can:

    Handle them on the same request

    Depends on your interface, if you have everything you need on a single page, you just send the data and handle it on a repository, which has access to all your models.

    An example that can be used for both could be:

    A book repository using Dependency Injection, which means that Book and Page will be automatically instantiated by 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);
        }
    
    }
    

    A Base Model with your validation:

    class Book extends BaseModel {
    
        public function validate($input)
        {
            /// validate here and return
        }   
    
    }
    

    Your models and rules for each:

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

    And then you create your view having book info and page info, and which will POST to 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');
        }
    
    }
    

    Again we are using Dependency Injection. $book_repository will be instantiated automatically. So your Controller doesn't need to know what a Book or a Page do, it just need to get the request and pass to a repository that will take care of everything.

    It's not all there, but it's a start.

    Handle them on different requests

    This is usual. User send a request, app check and store data. User send a second request, app check it all and send back errors, if needed.

    Handle them in background

    This is a smarter way to do it. Your app will receive all data, in one or more requests, store them, check them using a queue worker and send e-mails to the user telling him that there are some data to be filled. Books with no pages can be deleted after some time. You don't risk having bad data and your user will know what's missing as soon as you do too.

    已采纳该答案
    打赏 评论

相关推荐 更多相似问题