2012-04-17 30 views
8

Trong ứng dụng Symfony 2 của tôi, tôi có 3 vai trò người dùng khác nhau có thể được tiếp cận với một bộ phận quản lý backend:Bind một lộ trình để điều khiển khác nhau tùy thuộc vào vai trò người dùng

role_hierarchy: 
    ROLE_STAFF:  ROLE_USER 
    ROLE_MODERATOR: ROLE_STAFF 
    ROLE_ADMIN:  ROLE_MODERATOR 

Đối với một lộ trình như http://example.org/admin/post/, tôi muốn như ứng dụng của tôi hiển thị thông tin khác nhau tùy thuộc vào vai trò của người dùng, có nghĩa là 3 bộ điều khiển ràng buộc với một chỉ tuyến đường.

Cách tốt nhất để xử lý việc này là gì?

Tôi đã suy nghĩ về một số giải pháp nhưng không ai có vẻ là tốt cho tôi:

  1. Một bộ điều khiển, và trong mỗi hành động tôi chỉ thử nghiệm sử dụng vai trò:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         if ($this->get('security.context')->isGranted('ROLE_STAFF')) { 
          // Do ROLE_STAFF related stuff 
         } else if ($this->get('security.context')->isGranted('ROLE_MODERATOR')) { 
          // Do ROLE_MODERATOR related stuff 
         } else if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { 
          // Do ROLE_ADMIN related stuff 
         } 
    
         return array('posts' => $posts); 
        } 
    } 
    

    Thậm chí nếu điều đó không công việc, IMO rõ ràng đó không phải là một thiết kế tốt.

  2. Một BackendController rằng cử tới 3 bộ điều khiển khác nhau:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostBackendController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("", name="admin_post_index") 
        * @Template("AcmeBlogBundle:PostAdmin:index.html.twig") 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         if ($this->get('security.context')->isGranted('ROLE_STAFF')) { 
          $response = $this->forward('AcmeBlogBundle:PostStaff:index'); 
         } else if ($this->get('security.context')->isGranted('ROLE_MODERATOR')) { 
          $response = $this->forward('AcmeBlogBundle:PostModerator:index'); 
         } else if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { 
          $response = $this->forward('AcmeBlogBundle:PostAdmin:index'); 
         } 
    
         return $response; 
        } 
    } 
    

    Tương tự như số một.

  3. tôi đã cố gắng để làm cho bộ điều khiển mở rộng mỗi người khác:

    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostStaffController extends Controller 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_STAFF") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // Do ROLE_STAFF related stuff 
    
         return array('posts' => $posts); 
        } 
    } 
    
    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostModeratorController extends PostStaffController 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_MODERATOR") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // As PostModeratorController extends PostStaffController, 
         // I can either use parent action or redefine it here 
    
         return array('posts' => $posts); 
        } 
    } 
    
    <?php 
    
    /** 
    * @Route("/admin/post") 
    */ 
    class PostAdminController extends PostModeratorController 
    { 
        /** 
        * Lists all post entities. 
        * 
        * @Route("/", name="post_index") 
        * @Template() 
        * @Secure(roles="ROLE_ADMIN") 
        */ 
        public function indexAction() 
        { 
         $user = $this->get('security.context')->getToken()->getUser(); 
    
         // Same applies here 
    
         return array('posts' => $posts); 
        } 
    } 
    

    IMO đó là một thiết kế tốt hơn nhưng tôi không thể quản lý để làm cho nó hoạt động. Hệ thống định tuyến dừng trên bộ điều khiển đầu tiên nó khớp. Tôi muốn làm cho nó hành động vua của tầng phong cách tự động (tức là nếu người dùng là nhân viên sau đó đi đến PostStaffController, nếu không nếu người dùng là người điều hành đi đến PostModeratorController, nếu không đi đến PostAdminController).

  4. Thêm người nghe vào kernel.controller trong BlogBundle của tôi sẽ thực hiện công việc giống như số 2?

Tôi đang tìm thiết kế tốt nhất và giải pháp linh hoạt hơn có khả năng chúng tôi sẽ thêm nhiều vai trò hơn trong tương lai.

+0

Tôi đang đối mặt với tình huống tương tự, bạn đã tìm thấy giải pháp tốt chưa? –

+0

Tất cả các giải pháp đều tốt nhưng được thiết kế kém. Nếu bạn gặp phải vấn đề tương tự, trước tiên hãy đảm bảo rằng đó không phải là quan niệm sai về đơn đăng ký của bạn. Trong trường hợp của tôi thay vì làm điều này, tôi đã tạo 2 loại biểu mẫu khác cho thực thể của tôi: tức là "cấu hình" và "tuỳ chỉnh". Vì vậy, quản trị viên có thể truy cập bộ điều khiển "cấu hình" và "tùy chỉnh" trong khi nhân viên và người kiểm duyệt chỉ có thể truy cập "tuỳ chỉnh". Không chắc chắn nếu nó rõ ràng. Có lẽ tôi nên làm cho nó một câu trả lời hoàn chỉnh? – iamdto

+0

Bạn nói đúng, đó là một thiết kế tồi. Giải pháp của tôi là phân tách các khu vực khác nhau của ứng dụng trong các nhóm khác nhau, nơi tôi có thể quản lý các vai trò cho phù hợp. Cảm ơn. –

Trả lời

0

Phiên bản tự động của giải pháp thứ hai của bạn như thế nào? Giống như:

// Roles ordered from most to least significant (ROLE_ADMIN -> ROLE_MODERATOR -> etc) 
    $roles = $myUserProvider->getRoles(); 
    foreach ($roles as $role) { 
     // add a check to test, if the function you're calling really exists 
     $roleName = ucfirst(strtolower(mb_substr($role, 0, 5))); 
     $response = $this->forward(sprintf('AcmeBlogBundle:Post%s:index', $roleName)) 

     break; 
    } 

    // Check that $response is not null and do something with it ... 

Vì tôi không có thiết lập của bạn, tôi chưa thử nghiệm mã ở trên. Btw: sự khác biệt giữa phương pháp khác nhau để đăng nội dung nào đó?

+0

Giả sử bạn muốn xem bài đăng trong khu vực quản trị: bạn truy cập vào 'http://example.org/admin/post/ {id} '.Người kiểm duyệt sẽ có phiên bản nhẹ hơn, trong khi quản trị viên sẽ thấy ** tất cả thông tin về thực thể (người kiểm duyệt là khách hàng, quản trị viên là nhà phát triển và người từ công ty của chúng tôi). Tôi không muốn quá tải quan điểm của tôi, hình thức các loại, bộ điều khiển vv với rất nhiều 'if'' else' vì vậy đó là lý do tại sao tôi cần một điều phối viên của loại hình này. Không chắc đó có phải là một quyết định thiết kế hay không, nhưng đó là giải pháp duy nhất tôi đã tìm thấy vào lúc này. – iamdto

0

trong vendor/symfony/symfony/src/Symfony/Component/Routing/Router.php

Có một tùy chọn để thay thế cho matcher_class mà nên có thể trong config.yml.

Nếu bạn phân lớp UrlMatcher và ghi đè matchRequest sẽ được ưu tiên hơn so khớp Đường dẫn (chỉ url).

matchRequest mất một tham số $ yêu cầu (Request object)

Đối tượng Request nên chứa thông tin người dùng được cung cấp cho người nghe nhà cung cấp an ninh chạy trước khi người nghe router và cho phép bạn chọn các tuyến đường bằng cách kết hợp các URL và User Role. Các tuyến đường được lưu trữ trong một mảng được lập chỉ mục theo tên để các tên sẽ cần phải khác nhau.

Bạn có thể có thể sử dụng những cái tên như post_index[USER]post_index[STAFF]post_index[MODERATOR]

Để tạo ra các url với {{ path('post_index', {...}) }} bạn cũng sẽ cần phải thay thế các lớp con các URLGenerator và tiêm đó vào Router với các tùy chọn generator_class.

1

IMHO, Bạn không thể kích hoạt các bộ điều khiển khác nhau cho cùng một tuyến đường dựa trên vai trò. Đó chỉ là trách nhiệm khác nhau. Các tuyến đường dành cho bộ điều khiển chọn, vai trò dành cho các đặc quyền. Sau một năm bạn sẽ không nhớ lừa, ví dụ. khi bạn sẽ cố gắng thêm vai trò mới.

Tất nhiên vấn đề của nội dung khác nhau cho vai trò khác nhau là khá thường xuyên, vì vậy giải pháp ưa thích của tôi trong trường hợp này là:

  1. Khi bộ điều khiển cho vai trò khác nhau là rất khác nhau, tôi sử dụng các tuyến đường khác nhau với chuyển hướng khi cần thiết.
  2. Khi bộ điều khiển tương tự nhưng nội dung khác nhau, ví dụ: các điều kiện truy vấn cơ sở dữ liệu khác nhau, tôi sử dụng giải pháp tương tự như của bạn 2. nhưng thay vào đó là forwading, sử dụng các phương thức riêng/được bảo vệ từ cùng một bộ điều khiển để thực hiện công việc. Có một hack - Bạn phải kiểm tra vai trò từ trên xuống dưới, tức là. đầu tiên kiểm tra ROLE_ADMIN, tiếp theo ROLE_OPERATOR và ROLE_STAFF cuối cùng, bởi vì khi ROLE_ADMIN của bạn kế thừa từ ROLE_STAFF, sau đó chặn cho người dùng bắt nó.
  3. Khi sự khác biệt chỉ ở một số khối thông tin sẽ được hiển thị/ẩn cho các vai trò khác nhau, tôi ở lại với một bộ điều khiển và kiểm tra vai trò trong mẫu để xác định khối nào hiển thị hay không.
Các vấn đề liên quan