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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计