0

I have a plugin which allows Users to "buy" documents associated with an Entry. All it consists of is two fields on the User; a "Credits Available" text field, and an "Owned Documents" Entries field.

On the front end for each Entry I have a block of code which checks to see if the Entry being viewed is in the "Owned Documents" field or not. If it is, a message of "you own this" is shown, if not a form to "purchase" the document is shown. The code is this:

{% if currentUser %}
    {% set displayPurchaseButton = 'yes' %}

    {# Work out if we need to show the purchase button #}
    {% if currentUser.purchasedDocuments | length %}{# there are already purchased documents #}
        {% for document in currentUser.purchasedDocuments %}{# loop through all the currently purchased documents #}
            {% if document.id == entry.id %}{# The entry being viewed is currently purchased #}
                {% set displayPurchaseButton = 'no' %}
            {% endif %}
        {% endfor %}
    {% endif %}

    {% if displayPurchaseButton == 'yes' %}
        {% if currentUser.creditsAvailable < entry.creditCost %}{# They don't have enough to buy the document #}
            <p>Buy more credits</p>
        {% else %}{# Show the buy button #}
            <form action="" method="post" accept-charset="UTF-8" enctype="multipart/form-data" id="favourite">
                {{ getCsrfInput() }}
                <input type="hidden" name="action" value="usingCredits/credits/creditPurchase">
                <input type="hidden" name="documentId" value="{{ entry.id }}">
                <input type="hidden" name="redirect" value="{{ entry.url }}">

                <input type="submit" value="Buy Document ({{ entry.creditCost }} Cr)" class="favourite">
            </form>
        {% endif %}
    {% else %}
        <p>You own this document</p>
    {% endif %}
{% endif %}

This works... except immediately after 'buying' the document. In which case the reloaded page shows an updated Credit field (the cost of the purchase has been deducted from the Available Credits) but does not show the "you own this" message, instead showing the Purchase button. However, if I manually re-load the page, it behaves properly.

What's wrong?

Plugin code:

public function actionCreditPurchase()
{
    // basic security precautions
        $this->requirePostRequest();

    // Get info about the logged in user
        $user             = craft()->userSession->getUser();
        $ownedDocuments   = $user->getContent()->purchasedDocuments;
        $creditsAvailable = $user->getContent()->creditsAvailable;

    // what's the Entry ID for what they want to buy?
        $purchaseDocumentId = craft()->request->getPost('documentId');

    // does such a document exist?
        $document         = craft()->elements->getCriteria(ElementType::Entry);
        $document->id     = $purchaseDocumentId;
        $purchaseDocument = $document->first();

        if(!$purchaseDocument) {
            craft()->userSession->setError(Craft::t('An error occured: the document could not be found.'));
        }

    // check the user has the credits required
        $documentCost = $purchaseDocument->getContent()->creditCost;

        if($documentCost > $creditsAvailable) {
            craft()->userSession->setError(Craft::t('You do not have enough credits to purchase this document.'));
        }

    // also check that they don't already own the document
        $purchasedDocumentIds = $user->purchasedDocuments->ids();
        if( in_array($purchaseDocumentId, $purchasedDocumentIds) )
        {
            craft()->userSession->setError(Craft::t('You already own this document.'));
            $this->documentOwned = "yes";
        }

    // ok, buy the item
        if($purchaseDocument &&
           $documentCost < $creditsAvailable &&
           $this->documentOwned !== "yes")
        {
            // append the document ID to the existing array of owned documents
            array_push($purchasedDocumentIds, $purchaseDocumentId);

            // calculate the new creditsAvailable value
            $newCreditsAvailable = $creditsAvailable - $documentCost;

            // update the user fields
            $user->getContent()->setAttributes(array(
                'creditsAvailable' => $newCreditsAvailable,
                'purchasedDocuments' => $purchasedDocumentIds
            ));

            // save the user
            craft()->users->saveUser($user);
        }
}
1

Although submitting the form causes the same page to be loaded again, it turns out that Craft isn't actually re-parsing the loaded page. You have to tell it to do so.

So, if the plugin was successful you need to set it to redirect back to the URL that POSTed in the first place. Like so:

// save the user
$success = craft()->users->saveUser($user);

// redirect back to the original page if successful (as a new page request)
if($success) {
    $this->redirectToPostedUrl();
} else {
    craft()->userSession->setError(Craft::t('Could not purchase document.'));
}

Many thanks to Hubert Prein on Slack for that :)

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.