1. Fabrik 3.9 has been released. If you have updated Joomla to 3.9, this is a required update.
    Dismiss Notice

Triggering a new sequence number from a php script ?

Discussion in 'Community' started by lcollong, Jan 11, 2019.

  1. lcollong

    lcollong FabriKant d'applications web

    Level: Community
    Hi,

    I'm using the sequence element (on submit) for a custom CRM (offers, invoices, contracts, etc...). It's doing perfect as far as the new row follows the manual mechanism (add a new row, fill the elements and submit the form to create it).
    But in some situations, I need to create several of them from an external script (actually onAfterProcess php plugin from a specific form). I create the rows and the joined tables ones "by hand", directly in the DB. Not using the Fabrik's class/method.
    Is there any "easy way" to trigger a new sequence number from the script ? The "getSequence" method from the plugin class expects a complex array. Is there a way I can build this array and call the getSequence from a script in another form (not knowing about the sequence element params) ?

    Meanwhile, I did an horrible hack so that the first time the user see the newly created row (form view) the sequence element says "Assigned after form is submitted" and on the next submit the actual sequence number is calculated (onStoreRow around line 94) :
    PHP:

            $element = $this->getElement();
            $formModel = $this->getFormModel();
            $params = $this->getParams();
            $method = $params->get('sequence_method', 'load');
    //        if ($formModel->isNewRecord() && $method === 'submit')
            if (($formModel->isNewRecord() || $formModel->getElementData($element->name) == JText::_('PLG_ELEMENT_SEQUENCE_ASSIGNED_AFTER_SUBMIT')) && $method === 'submit')
            {
                $formData = json_decode(json_encode($formModel->formDataWithTableName));
                $this->swapValuesForLabels($formData);
                $this->setStoreDatabaseFormat($formData, $repeatCounter);
                $data[$element->name] = $data[$element->name . '_raw'] = $this->getSequence($formData);
            }
     
    Definitively not something for which I could make a github's PR ;-)

    Other point :
    Is there any underlying mechanism to prevent two "getSequence" updating/inserting the table on the same time with the same number (such as the new lock feature) ?
    Should I check by myself that the sequence number is effectively unique (or add a SQL constraint) ?

    Thanks,
     
  2. cheesegrits

    cheesegrits Support Gopher Staff Member

    Level: Community
    I'm not entirely understanding what you are trying to do, but I can answer some questions.

    Hmmm. Probably. However, you would have to load an instance of the other form, and get the sequence element model ...

    Code (Text):

    // get a form model instance
    $otherForm = JModelLegacy::getInstance('Form', 'FabrikFEModel');
    // set the ID for the other form
    $otherForm->setId(123);
    // grab the sequence element model by name
    $sequenceElement = $otherForm->getElement('othertable___sequence');
    // or by id ...
    // $sequenceElement = $otherForm->getElement(345, true);
     
    Then you can call $elementModel->getSequence(). Which brings up your next question ...

    I presume you aren't using PK mode, in which case we still pretty much take care of that - we store the sequence in the #__fabrik_sequence table. Yes, there is a very, very slim chance that there could be a collision. But the window for that is tiny ... the submissions would have to be running within a handful of milleseconds of each other. And actually, about the same chance as lockrow failing, as it uses the same technique of gating using a database.

    So ... calling getSequence(), not using PK mode, all you need to pass it is an array with any placeholders you use in the 'affix' ...

    Code (Text):

    $sequence = $sequenceElement->getSequence(array('othertable___somethingused_in_affix' => 'foo'));
     
    If you don't use placeholders in the affix, you can just pass it an empty array.

    If you are using PK mode (which from your description I don't think you can be), you would also need to add the rowid to the array, array('rowid' => '123', 'othertable___whatever' => 'foo').

    -- hugh
     
  3. lcollong

    lcollong FabriKant d'applications web

    Level: Community
    Thanks for these very valuable explanation which could be added to the wiki ?
    I've had to set the getSequence function as "public" rather than "private" in plugins/fabrik_element/sequence/sequence.php as it's throw an error due to not being in the right context. Now, it's perfectly working.

    You're right, I'm not using PK as we wish to reset the numbering every 1st of January (either "flushing" the fabrik_sequence table - did not try - , either using a "year" placeholder in the numbering scheme as per the wiki example).

    Indeed, I don't think the risk of having twice the same number is high enough to justify taking care of it for now. However, it could be a subject in some heavy used app. Could be solved using a column unique constraint in SQL or by checking after saving the record that the number is really unique.... we'll see if it happens !

    The reason why I need to achieve this is because we use two different ways of creating an offer (which is build as an invoice with a main table row and several joined items rows from itemlist table). The first one is the regular one for which the standard features works perfectly. The second way is using a cart together with a fancy product list (images) to let the user build his offer the way he wants with some automated process and "easy to understand" ways to go. At the end, there is a button "build the offer" which create the actual offer (main table), convert the cart into itemlist (joined table) and open the resulting form for modification, and submission (which build the pdf). We need to save the cart without actually create the offer... hence the way I went.

    As a side question, my script code to create the offer body (main table) looks like this :
    PHP:

        public function create() {
            $db = JFactory::getDbo();      
            $now = date('Y-m-d H:i:s');
           
            // Attribuer numéro devis
            $fabrikFormDevis = JModelLegacy::getInstance('Form', 'FabrikFEModel');
            $fabrikFormDevis->setId(_FABRIK_DEVIS_FORM_ID);
            $sequenceElement = $fabrikFormDevis->getElement(_FABRIK_DEVIS_SEQUENCE_ELEMENT);
            $newNumber = $sequenceElement->getSequence(array());      
           
            $dev = new stdClass();
            $dev->dev_num                = $newNumber;
            $dev->dev_ref_projet        = $this->dev_ref_projet;
            $dev->dev_ref_organisation    = $this->dev_ref_organisation;      
            $dev->dev_ref_contact        = $this->dev_ref_contact;      
            $dev->dev_creation_time        = $now;
            $dev->dev_modif_time        = $now;  
            $dev->dev_creation_user        = $this->currentUser;      
            $dev->dev_date_devis        = $now;  
            $dev->dev_statut            = _STATUT_DEVIS_BROUILLON;  
           
           
            try {
                $result = $db->insertObject('devis', $dev, 'dev_id');
            }
            catch (Exception $e) {
                JLog::add('Création devis. Erreur : '.$e->getMessage(), JLog::ERROR, 'Synapse');                          
                return false;
            }
           
            $this->dev_id = $dev->dev_id;
            return $this->dev_id;
        }
     
    Since we are now loading the form instance to set the number, there is probably a better way to populate the elements and actually create the record using the "fabrikFormDevis" object rather than doing by "hand" as I did ?
     

Share This Page