Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 618093
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 13, 20262026-05-13T18:30:26+00:00 2026-05-13T18:30:26+00:00

For example, there’s remote API with the following calls: getGroupCapacity(group) setGroupCapacity(group, quantity) getNumberOfItemsInGroup(group) addItemToGroup(group,

  • 0

For example, there’s remote API with the following calls:

getGroupCapacity(group)
setGroupCapacity(group, quantity)
getNumberOfItemsInGroup(group)
addItemToGroup(group, item)
deleteItemFromGroup(group, item)

The task is to add some item to some group. Groups have capacity.
So first we should check if group is not full. If it is, increase capacity, then add item. Something like this (for example API is exposed with SOAP):

function add_item($group, $item) {
   $soap = new SoapClient(...);
   $capacity = $soap->getGroupCapacity($group);
   $itemsInGroup = $soap->getNumberOfItemsInGroup($group);
   if ($itemsInGroup == $capacity) {
       $soap->setGroupCapacity($group, $capacity + 1);
   }
   $soap->addItemToGroup($group, $item);
}

Now what if addItemToGroup failed (item was bad)? We need to rollback group’s capacity.

Now imagine that you have to add 10 items to group and then setup added items with some properties – and all this in a single transaction. That means if it fails somewhere in the middle you must rollback everything to previous state.

Is it possible without bunch of IF’s and spaghetti code? Any library, framework, pattern, or architecture decision which will simplify such operations (in PHP)?

UPD: SOAP is just an example. Solution should fit any service, even raw TCP. The main point of the question is how to organize transactional behavior with underlying non-transactional API.

UPD2: I guess this problem is pretty same in all programming languages. So any answers are welcomed, not only PHP.

Thanks in advance!

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-13T18:30:27+00:00Added an answer on May 13, 2026 at 6:30 pm
    <?php
    //
    // Obviously better if the service supports transactions but here's
    // one possible solution using the Command pattern.
    //
    // tl;dr: Wrap all destructive API calls in IApiCommand objects and
    // run them via an ApiTransaction instance.  The IApiCommand object
    // provides a method to roll the command back.  You needn't wrap the
    // non-destructive commands as there's no rolling those back anyway.
    //
    // There is one major outstanding issue: What do you want to do when
    // an API command fails during a rollback? I've marked those areas
    // with XXX.
    //
    // Barely tested but the idea is hopefully useful.
    //
    
    class ApiCommandFailedException extends Exception {}
    class ApiCommandRollbackFailedException extends Exception {}
    class ApiTransactionRollbackFailedException extends Exception {}
    
    interface IApiCommand {
        public function execute();
        public function rollback();
    }
    
    
    // this tracks a history of executed commands and allows rollback    
    class ApiTransaction {
        private $commandStack = array();
    
        public function execute(IApiCommand $command) {
            echo "EXECUTING " . get_class($command) . "\n";
            $result = $command->execute();
            $this->commandStack[] = $command;
            return $result;
        }
    
        public function rollback() {
            while ($command = array_pop($this->commandStack)) {
                try {
                    echo "ROLLING BACK " . get_class($command) . "\n";
                    $command->rollback();
                } catch (ApiCommandRollbackFailedException $rfe) {
                    throw new ApiTransactionRollbackFailedException();
                }
            }
        }
    }
    
    
    // this groups all the api commands required to do your
    // add_item function from the original post.  it demonstrates
    // a nested transaction.
    class AddItemToGroupTransactionCommand implements IApiCommand {
        private $soap;
        private $group;
        private $item;
        private $transaction;
    
        public function __construct($soap, $group, $item) {
            $this->soap = $soap;
            $this->group = $group;
            $this->item = $item;
        }
    
        public function execute() {
            try {
                $this->transaction = new ApiTransaction();
                $this->transaction->execute(new EnsureGroupAvailableSpaceCommand($this->soap, $this->group, 1));
                $this->transaction->execute(new AddItemToGroupCommand($this->soap, $this->group, $this->item));
            } catch (ApiCommandFailedException $ae) {
                throw new ApiCommandFailedException();
            }
        }
    
        public function rollback() {
            try {
                $this->transaction->rollback();
            } catch (ApiTransactionRollbackFailedException $e) {
                // XXX: determine if it's recoverable and take
                //      appropriate action, e.g. wait and try
                //      again or log the remaining undo stack
                //      for a human to look into it.
                throw new ApiCommandRollbackFailedException();
            }
        }
    }
    
    
    // this wraps the setgroupcapacity api call and
    // provides a method for rolling back    
    class EnsureGroupAvailableSpaceCommand implements IApiCommand {
        private $soap;
        private $group;
        private $numItems;
        private $previousCapacity;
    
        public function __construct($soap, $group, $numItems=1) {
            $this->soap = $soap;
            $this->group = $group;
            $this->numItems = $numItems;
        }
    
        public function execute() {
            try {
                $capacity = $this->soap->getGroupCapacity($this->group);
                $itemsInGroup = $this->soap->getNumberOfItemsInGroup($this->group);
                $availableSpace = $capacity - $itemsInGroup;
                if ($availableSpace < $this->numItems) {
                    $newCapacity = $capacity + ($this->numItems - $availableSpace);
                    $this->soap->setGroupCapacity($this->group, $newCapacity);
                    $this->previousCapacity = $capacity;
                }
            } catch (SoapException $e) {
                throw new ApiCommandFailedException();
            }
        }
    
        public function rollback() {
            try {
                if (!is_null($this->previousCapacity)) {
                    $this->soap->setGroupCapacity($this->group, $this->previousCapacity);
                }
            } catch (SoapException $e) {
                throw new ApiCommandRollbackFailedException();
            }
        }
    }
    
    // this wraps the additemtogroup soap api call
    // and provides a method to roll the changes back
    class AddItemToGroupCommand implements IApiCommand {
        private $soap;
        private $group;
        private $item;
        private $complete = false;
    
        public function __construct($soap, $group, $item) {
            $this->soap = $soap;
            $this->group = $group;
            $this->item = $item;
        }
    
        public function execute() {
            try {
                $this->soap->addItemToGroup($this->group, $this->item);
                $this->complete = true;
            } catch (SoapException $e) {
                throw new ApiCommandFailedException();
            }
        }
    
        public function rollback() {
            try {
                if ($this->complete) {
                    $this->soap->removeItemFromGroup($this->group, $this->item);
                }
            } catch (SoapException $e) {
                throw new ApiCommandRollbackFailedException();
            }
        }
    }
    
    
    // a mock of your api
    class SoapException extends Exception {}
    class MockSoapClient {
        private $items = array();
        private $capacities = array();
    
        public function addItemToGroup($group, $item) {
            if ($group == "group2" && $item == "item1") throw new SoapException();
            $this->items[$group][] = $item;
        }
    
        public function removeItemFromGroup($group, $item) {
            foreach ($this->items[$group] as $k => $i) {
                if ($item == $i) {
                    unset($this->items[$group][$k]);
                }
            }
        }
    
        public function setGroupCapacity($group, $capacity) {
            $this->capacities[$group] = $capacity;
        }
    
        public function getGroupCapacity($group) {
            return $this->capacities[$group];
        }
    
        public function getNumberOfItemsInGroup($group) {
            return count($this->items[$group]);
        }
    }
    
    // nested transaction example
    // mock soap client is hardcoded to fail on the third additemtogroup attempt
    // to show rollback
    try {
        $soap = new MockSoapClient();
        $transaction = new ApiTransaction();
        $transaction->execute(new AddItemToGroupTransactionCommand($soap, "group1", "item1")); 
        $transaction->execute(new AddItemToGroupTransactionCommand($soap, "group1", "item2"));
        $transaction->execute(new AddItemToGroupTransactionCommand($soap, "group2", "item1"));
        $transaction->execute(new AddItemToGroupTransactionCommand($soap, "group2", "item2"));
    } catch (ApiCommandFailedException $e) {
        $transaction->rollback();
        // XXX: if the rollback fails, you'll need to figure out
        //      what you want to do depending on the nature of the failure.
        //      e.g. wait and try again, etc.
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Take the following window layout for example: There is a Grid element defined. It
Is there a way to write custom comparator, following this example: There are at
In the following example there is no horizontal scrollbar shown. As soon as I
I have the following markup (this is an example there are hundreds) <div class=Q3>
I will explain problem with an example: There is two table in my database,
On the Groovy example page there is an example of how to use Groovy
I need to explain this by example: Is there a best practice or preference
Is a C# struct thread-safe? For example if there is a: struct Data {
Is there good example code or a test project for explaining the Model–view–presenter (MVP)
Is there an example of an OAuth implementation or profile which uses multiple authorization

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.