2016-06-10 2 views
1

현재 개인 휴식 API를 개발 중입니다. 상위 레벨 리소스를 작성하기 위해 게시 할 때 하위 리소스의 코드 종속성 문제가 발생합니다. 이 나머지 API를 REST API - 공동 종속 엔드 포인트 드라이 런 흐름?

/products/[PRODUCT_ID]/categories 
/products/[PRODUCT_ID]/media 
/products/[PRODUCT_ID]/shippingZones 
/products/[PRODUCT_ID]/variants 

예를 들어

..

I 다음 상위 엔드 포인트가 ..

/products 

그리고/제품에 대한 다음과 같은 자식 엔드 포인트의 상대 ..

,/제품에는 'categories', 'media', 'shippingZones', 'variant'와 같은 키에 대해 게시 된 페이로드를 수락 할 수도 있습니다.

이러한 키가 설정되면/products 끝 점이 드릴 다운되고/products의 현재 게시 요청에 대한 페이로드 키를 기반으로 하위 리소스가 만들어지고 연결됩니다.

다음은/products에 대한 POST 요청에서 실행되는 코드로, 현재 처리되는 방식을 보여줍니다. 아래에서 한눈에 살펴본 후 문제를 직접 살펴 보겠습니다. 설명하기 전에 문제를보실 수 있을까요?

protected function post() 
    { 
     if (!$this->validatePermissions()) { 
      return; 
     } 

     $productsM = new productsModel(); 
     $filesM = new filesModel(); 

     $userId = $this->controller->user['id']; 
     $productId = $this->getResourceIdByName('products'); 

     $productCategories = $this->controller->payload['productCategories']; 
     $productMedia = $this->controller->payload['productMedia']; 
     $productShippingZones = $this->controller->payload['productShippingZones']; 
     $productVariants = $this->controller->payload['productVariants']; 

     $existingProduct = ($productId) ? $productsM->getSingle(array('id' => $productId, 'userId' => $userId)) : array(); 
     $product = array_merge($existingProduct, $this->controller->getFilteredPayload(array(
      'title', 
      'description', 
      'shippingType', 
      'fileId', 
      'hasVariants', 
      'isHidden' 
     ))); 

     $this->validateParameters(array('title' => $product['title'])); 

     if ($productId && !$existingProduct) { 
      $this->addResponseError('productId'); 
     } 

     if ($product['shippingType'] && !in_array($product['shippingType'], array('free', 'flat', 'calculated'))) { 
      $this->addResponseError('shippingType'); 
     } 

     if ($product['fileId'] && !$filesM->getNumRows(array('id' => $product['fileId'], 'userId' => $userId))) { 
      $this->addResponseError('fileId'); 
     } 

     if ($this->hasResponseErrors()) { 
      return; 
     } 

     $lastCreatedProduct = (!$existingProduct) ? $productsM->getSingle(array('userId' => $userId), array('publicId' => 'DESC')) : array(); 

     $product = $productsM->upsert(array('id' => $productId, 'userId' => $userId), array_merge($product, array(
      'publicId' => $lastCreatedProduct['publicId'] + 1, 
      'userId' => $userId, 
      'isActive' => 1, 
      'modified' => time(), 
      'created' => time() 
     )), array('publicId')); 

     // product categories subresource 
     if (is_array($productCategories)) { 
      foreach ($productCategories as $index => $productCategory) { 
       $endpoint = "/products/{$product['id']}/categories/{$productCategory['id']}"; 
       $requestMethod = ($productCategory['isActive'] !== '0') ? endpoint::REQUEST_METHOD_POST : endpoint::REQUEST_METHOD_DELETE; 

       $productCategory = $this->executeEndpointByPath($endpoint, $requestMethod, $productCategory); 
       foreach ($productCategory['errors'] as $error) { 
        $this->addResponseError($error['parameter'], $error['message'], array('productCategories', $index, $error['parameter'])); 
       } 

       $product['productCategories'][$index] = $productCategory['data']; 
      } 
     } 

     // product media subresource 
     if (is_array($productMedia)) { 
      foreach ($productMedia as $index => $media) { 
       $endpoint = "/products/{$product['id']}/media/{$media['id']}"; 
       $requestMethod = ($media['isActive'] !== '0') ? endpoint::REQUEST_METHOD_POST : endpoint::REQUEST_METHOD_DELETE; 

       $media = $this->executeEndpointByPath($endpoint, $requestMethod, $media); 
       foreach ($media['errors'] as $error) { 
        $this->addResponseError($error['parameter'], $error['message'], array('productMedia', $index, $error['parameter'])); 
       } 

       $product['productMedia'][$index] = $media['data']; 
      } 
     } 

     // product shipping zones subresource 
     if (is_array($productShippingZones)) { 
      foreach ($productShippingZones as $index => $productShippingZone) { 
       $endpoint = "/products/{$product['id']}/shippingZones/{$productShippingZone['id']}"; 
       $requestMethod = ($productShippingZone['isActive'] !== '0') ? endpoint::REQUEST_METHOD_POST : endpoint::REQUEST_METHOD_DELETE; 

       $productShippingZone = $this->executeEndpointByPath($endpoint, $requestMethod, $productShippingZone); 
       foreach ($productShippingZone['errors'] as $error) { 
        $this->addResponseError($error['parameter'], $error['message'], array('productShippingZones', $index, $error['parameter']));     
       } 

       $product['productShippingZones'][$index] = $productShippingZone['data']; 
      } 
     } 

     // product variants subresource 
     if (is_array($productVariants)) { 
      foreach ($productVariants as $index => $productVariant) { 
       $endpoint = "/products/{$product['id']}/variants/{$productVariant['id']}"; 
       $requestMethod = ($productVariant['isActive'] !== '0') ? endpoint::REQUEST_METHOD_POST : endpoint::REQUEST_METHOD_DELETE; 

       $productVariant = $this->executeEndpointByPath($endpoint, $requestMethod, $productVariant); 
       foreach ($productVariant['errors'] as $error) { 
        $this->addResponseError($error['parameter'], $error['message'], array('productVariants', $index, $error['parameter'])); 
       } 

       $product['productVariants'][$index] = $productVariant['data']; 
      } 
     } 

     return $product; 
    } 

좋아! 이제 문제. 이 흐름을 사용하면 새로 생성 된 제품의/products에 대한 하위 리소스 생성은 제품 데이터베이스 테이블에 삽입 된 새 행에 종속되어 반복 실행 및 하위 리소스 생성에 앞서 제품 ID를 반환합니다. 하위 리소스는 엔드 포인트 uri에 productId를 전달하지 않았습니다.

이것은 코드 종속성의 문제를 야기하고 모든 또는 다른 원칙을 유지합니다.

새 제품이 생성되고 초기/제품 오류 검사가 완료되면 제품은 제품 데이터베이스 테이블에서 새 행을 가져옵니다. 그러나이 작업이 완료된 후 하위 리소스 생성 초기 요청에서 전달 된 데이터로 인해 모든 하위 리소스 생성이 실패하면 초기 요청은 부분적으로 만 성공합니다. 하위 리소스의 오류로 인해 특별히 오류가있는 하위 리소스가 만들어져 처음 생성 된 제품과 연결되는 것을 방지 할 수 있습니다.

그래서 여기에 .. 내 아이디어 몇 가지의

나는 잠재적으로 완전히 삽입/업데이트를 무시하고 있는지 확인하기 위해 처리하는 모든 부모/자식 엔드 포인트 오류를 ​​통해 데이터를 실행하는 드라이 런 방식을 구현하고 싶습니다

데이터가 깨끗합니다. 코드 플로우의 가독성을 과도하게 복잡하게하거나 깨뜨리지 않고 엔드 포인트의 플로우에이를 병합하는 방법을 확신 할 수 없습니다.

다른 방법으로 생각해 보거나 실행 흐름을 변경하면 이것이 최선의 접근 방식의 올바른 방향을 지적하게 될 것입니다.

감사합니다.

답변

1

문제는 트랜잭션 처리입니다. 궁극적으로 트랜잭션과 롤백을 처리해야합니다. 그렇습니다. ID를 다시 가져와야하기 때문에 PHP 프로그램에서 문제가 될 수 있습니다.하지만 실제로 트랜잭션 인 경우 제품이 먼저 삽입되지 않으면 실패해야합니다.

하나의 옵션을 사용하면 트랜잭션 처리를 지속성 계층 (데이터베이스?)으로 푸시하는 것이 더 쉽습니다.프로 시저를 통과 또는 실패로 설정 한 다음 발생한 문제를 나타내는 적절한 코드를 다시 보낼 수 있으므로 저장 프로 시저가 여기에 하나의 옵션입니다 (필요한 경우 오류 정보도 포함될 수 있음). 관계형 데이터베이스는 일반적으로이 유형의 처리를 구현하는 비교적 쉬운 방법을 제공하며 NoSQL은 적중 또는 누락되었습니다.

이 시나리오에서는 PHP가 단순 해지지 만 일부 데이터베이스 코드를 유지 관리하기 시작합니다.

마른 실행을 설정하고 제한 조건을 풀어서 데이터를 실행할 수 있습니다. 이런 식으로 할 수있는 가치가있을 수 있습니다. 그러나 여전히 직면하게 될 "시스템이 제대로 작동 할 것인가"문제는 해결되지 않습니다. 접근법에 가치가 있는지 여부는 운동에서 얻게 될 정보에 달려 있습니다. 제대로 수행하면 몇 가지 가치를 산출 할 수있는 준 단위 테스트가 생성됩니다.

+0

필자는 PHP MySQLi 클래스 트랜잭션 메소드를 사용하고 스크립트 실행시 ID 종속성을 지원하면서 실행 중 임의의 단계에서 트랜잭션 중에 오류가 발생하면 롤백 지원을 구축했습니다. 한 돌로 2 마리의 새를 풀 었다고 나는 믿는다. –

+0

소프트웨어의 궁극적 인 테스트는 "효과가 있습니까?"입니다. 네가 작동하는 시나리오를 찾은 것처럼 들린다. –

관련 문제