6

हमने हाल ही में संगठन सुधारने, डुप्लिकेशंस को कम करने, अन्य चीजों के साथ, सिद्धांत सुधार 2.2 और ज़ेंड फ्रेमवर्क 2 के हिस्सों का उपयोग करना शुरू कर दिया है। आज, मैंने अपने नियंत्रकों और सिद्धांत संस्थाओं के मध्य मध्यस्थ के रूप में कार्य करने के लिए एक सेवा परत को लागू करने के लिए विचारों को फेंकना शुरू कर दिया।सेवा परत (सिद्धांत और जेडएफ) में डेटा एक्सेस और सुरक्षा

अभी, हमारे अधिकांश तर्क नियंत्रक में रहते हैं। इसके अलावा, हम कुछ अनुमतियों के परीक्षण के लिए एक एक्शन हेल्पर का उपयोग करते हैं; हालांकि, मैं ज़ेंड \ Di को लागू करने के बाद एक नए दृष्टिकोण के साथ आया था। मैंने इकाई-विशिष्ट सेवा मॉडल बनाना शुरू किया, जो एक EntityManager उदाहरण इंजेक्ट करने के लिए ज़ेंड \ Di का उपयोग करते हैं, और वर्तमान उपयोगकर्ता की अनुमतियां।

नियंत्रक कोड इस प्रकार है:

class Project_DeleteController extends Webjawns_Controller_Action 
{ 
    public function init() 
    { 
     $this->_initJsonContext(); 
    } 

    public function indexAction() 
    { 
     $response = $this->_getAjaxResponse(); 

     $auditId = (int) $this->_getParam('audit_id'); 
     if (!$auditId) { 
      throw new DomainException('Audit ID required'); 
     } 

     /* @var $auditService Service\Audit */ 
     $auditService = $this->getDependencyInjector()->get('Service\Audit'); 

     try { 
      $auditService->delete($auditId); 
      $response->setStatusSuccess(); 
     } catch (Webjawns\Exception\SecurityException $e) { 
      $this->_noAuth(); 
     } catch (Webjawns\Exception\Exception $e) { 
      $response->setStatusFailure($e->getMessage()); 
     } 

     $response->sendResponse(); 
    } 
} 

और हमारी सेवा परतों में से एक का एक उदाहरण। कन्स्ट्रक्टर दो मानकों को लेता है - एक EntityManager लेता है, और दूसरा एक इकाई \ UserAccess ऑब्जेक्ट - Zend \ Di द्वारा इंजेक्शन।

namespace Service; 

use Webjawns\Service\Doctrine, 
    Webjawns\Exception; 

class Audit extends AbstractService 
{ 
    public function delete($auditId) 
    { 
     // Only account admins can delete audits 
     if (\Webjawns_Acl::ROLE_ACCT_ADMIN != $this->getUserAccess()->getAccessRole()) { 
      throw new Exception\SecurityException('Only account administrators can delete audits'); 
     } 

     $audit = $this->get($auditId); 

     if ($audit->getAuditStatus() !== \Entity\Audit::STATUS_IN_PROGRESS) { 
      throw new Exception\DomainException('Audits cannot be deleted once submitted for review'); 
     } 

     $em = $this->getEntityManager(); 
     $em->remove($audit); 
     $em->flush(); 
    } 

    /** 
    * @param integer $auditId 
    * @return \Entity\Audit 
    */ 
    public function get($auditId) 
    { 
     /* @var $audit \Entity\Audit */ 
     $audit = $this->getEntityManager()->find('Entity\Audit', $auditId); 
     if (null === $audit) { 
      throw new Exception\DomainException('Audit not found'); 
     } 

     if ($audit->getAccount()->getAccountId() != $this->getUserAccess()->getAccount()->getAccountId()) { 
      throw new Exception\SecurityException('User and audit accounts do not match'); 
     } 

     return $audit; 
    } 
} 
  1. इस हम क्या हासिल करने की कोशिश कर रहे हैं के लिए उपयोग करने के लिए एक उचित पैटर्न है?
  2. क्या पोस्ट लेयर के भीतर अनुमति परत के भीतर अनुमति सत्यापन होना अच्छा अभ्यास है?
  3. जैसा कि मैं इसे समझता हूं, तर्क को अभी भी नियंत्रक में रहता है, जिससे विभिन्न संदर्भों (जेएसओएन, एक्सएमएल, एचटीएमएल इत्यादि) में मॉडल लचीलापन का उपयोग किया जा सकता है। विचार?

मैं इस तरह से काम करने के तरीके से खुश हूं, लेकिन अगर कोई यह कैसे कर रहा है, तो कोई भी नकारात्मक पक्ष देखता है, तो कृपया अपने विचार पोस्ट करें।

+1

बस मेरे दो प्रमाणीकरण पर पेंस।मुझे विश्वास नहीं है कि एक सही तरीका और गलत तरीका है, हालांकि, मैंने सेवा प्रमाणीकरण में अपना प्रमाणीकरण डालना शुरू कर दिया, लेकिन फिर इसे मेरे नियंत्रकों में स्थानांतरित कर दिया। मेरा तर्क यह था कि सेवा परत मेरी आंतरिक एपीआई है और मुझे दुनिया को बेनकाब करने के लिए अपने नियंत्रक परत का उपयोग करना चाहिए, इसलिए यह तय करना चाहिए कि किसके पास पहुंच है। अगर मैं किसी भी आंतरिक उपकरण/स्क्रिप्ट आदि बनाना चाहता हूं, तो मुझे अपनी सेवा परत का उपयोग करने के लिए उनमें प्रमाणीकरण बनाने की आवश्यकता नहीं है। –

+0

प्रमाणीकरण और अभिगम नियंत्रण को मिश्रण न करने के लिए सावधान रहें। किसी भी डोमेन क्लास को चित्र में लाए जाने से पहले प्रमाणीकरण मॉड्यूल में जा सकता है (चाहिए?)। केवल एक बार जब आप उपयोगकर्ता पहचान स्थापित कर लेंगे। उदाहरण: अगर (! $ AuthService-> है Identity()) आपके डोमेन सेवा मॉडल के अंदर। – dualmon

+1

@ जैमी सुथरलैंड: मैं असहमत हूं। सेवाएं व्यापार तर्क को परिभाषित करती हैं; नियंत्रक अनुरोध और उचित व्यापार तर्क के बीच पुल हैं। उदाहरण के लिए, आपके पास उत्पाद को ऑर्डर करने के लिए केवल एक ही सेवा होगी, लेकिन आपके पास HTTP अनुरोध, API अनुरोध, और बहुत कुछ के लिए एकाधिक नियंत्रक हो सकते हैं। यदि आप एसीएल से विशिष्ट अनुरोध करने के लिए चिंतित हैं (उदाहरण के लिए, HTTP के माध्यम से आप एक उपयोगकर्ता सत्र की अपेक्षा कर सकते हैं जहां आप एपीआई अनुरोधों के लिए एक गुप्त कुंजी की अपेक्षा करेंगे), इसके लिए अनुमति देने के लिए अपने एसीएल कार्यान्वयन को सामान्यीकृत करें। – moteutsch

उत्तर

1

मुझे यह पसंद है कि आप यहां क्या कर रहे हैं, और मुझे लगता है कि आपकी चिंताओं को अलग करना अच्छा है। हम कस्टम रेपॉजिटरीज़ का उपयोग करके इसे एक कदम आगे ले जाने के साथ प्रयोग कर रहे हैं। तो, उदाहरण के लिए जहां एक मानक मॉडल/सेवा विधि कुछ ऐसा दिखाई देगा के लिए:

public function findAll($sort = null) 
{ 
    if (!$sort) $sort = array('name' => 'asc'); 
    return $this->getEm()->getRepository('Application\Entity\PartType') 
       ->findAll($sort); 

} 

... हम चीजों भंडार को DQL की आवश्यकता है कि जोड़ रहे हैं, मॉडल से बाहर सभी DQL रखने के लिए, उदाहरण के लिए:

public function findAllProducts($sort = null) 
{ 
    if (!$sort) $sort = array('name' => 'asc'); 
    return $this->getEm()->getRepository('Application\Entity\PartType') 
       ->findAllProducts($sort); 

} 

ऊपर मॉडल के लिए, भंडार वर्ग इस तरह दिखता है:

<?php 
namespace Application\Repository; 

use Application\Entity\PartType; 
use Doctrine\ORM\EntityRepository; 

class PartTypeRepository extends EntityRepository 
{ 

    public function findAllProducts($order=NULL) 
    { 
     return $this->_em->createQuery(
        "SELECT p FROM Application\Entity\PartType p 
         WHERE p.productGroup IS NOT NULL 
         ORDER BY p.name" 
       )->getResult(); 
    } 

} 

ध्यान दें कि हम केवल सिद्धांत विस्तार किया है \ ORM \ जो EntityRepository मतलब यह है कि हम सभी को फिर से परिभाषित करने की जरूरत नहीं हैमानक सिद्धांत भंडार विधियों, लेकिन यदि आवश्यकता हो तो हम उन्हें ओवरराइड कर सकते हैं, और हम अपने स्वयं के कस्टम जोड़ सकते हैं।

तो एक्सेस नियंत्रण के संबंध में, यह हमें रिपोजिटरी से आपकी सेवाओं में व्यावसायिक तर्क तक पहुंचने से बहुत कम स्तर पर पहचान-आधारित बाधाओं या अन्य रिकॉर्ड-स्तरीय स्थितियों को जोड़ने की क्षमता देता है। इस तरह से, सेवाओं को कार्यान्वयन से अनजान हैं। जब तक हम ऐप के अन्य हिस्सों में डीक्यूएल डालने के बारे में सख्त नहीं हैं, हम किसी भी वर्ग के लिए रिकॉर्ड-स्तरीय व्यावसायिक बाधाओं को प्राप्त कर सकते हैं जो भंडार के माध्यम से डेटाबेस तक पहुंचता है। (ऐप के उच्च स्तर में कस्टम डीक्यूएल के लिए देखें)।

उदाहरण:

public function findAll($order=NULL) 
    { 
     // assumes PHP 5.4 for trait to reduce boilerplate locator code 
     use authService; 

     if($this->hasIdentity()) { 
      return $this->_em->createQuery(
         "SELECT p FROM Application\Entity\PartType p 
          JOIN p.assignments a 
          WHERE a.id = " . $this->getIdentity()->getId() 
        )->getResult(); 
     } else { 
      return NULL; 
     } 
    } 
+1

मैं भंडार में एक्सेस-कंट्रोल कोड रखने के साथ दृढ़ता से असहमत हूं। भंडार को आवेदन के व्यावसायिक तर्क के बारे में कुछ नहीं पता होना चाहिए। इसे केवल डेटा पुनर्प्राप्त करने के साथ ही चिंतित होना चाहिए। व्यापार तर्क उच्च स्तरीय सेवाओं और कक्षाओं के लिए है। – moteutsch

+0

मुझे लगता है कि सिद्धांत कार्यान्वयन से भंडारों को सारणी देना भी एक अच्छा विचार है। विरासत के बजाय संरचना (डिफ़ॉल्ट सिद्धांत भंडार वर्गों) का उपयोग करें। इंटरफ़ेस में कोडिंग के साथ यह आपको कुछ या सभी रिपॉजिटरीज़ में डेटा स्रोतों को आसानी से स्वैप करने की अनुमति देता है (जिसे मैं "मैपर" कहता हूं, "डॉक्टर रिपॉजिटरीज़ के साथ भ्रम से बचने के लिए)। – moteutsch