0

Please help me solve the problem. Task: to organize the unloading of reviews from a third-party service that gives reviews in the form of xml.

I solved this problem through the module. I did all the necessary actions to process the received data in the controller. Here is the controller code:

<?php
namespace modules\reviews\controllers;

use Craft;
use craft\web\Controller;
use craft\elements\Entry;


class ReviewsController extends Controller
{

    protected array|bool|int $allowAnonymous = true;

    public function actionStart() {

        $xml="XMLURL";
        $xml= simplexml_load_file($xml);

        foreach($xml->reviews->review as $review){

            $id = $review->attributes()->id;
            $text= $review->text;

            $entries = Entry::find()->section('reviews')->search('reviewId::'.$id)->all();

            if(count($entries) == 0){
                $entry = new Entry();
                $entry->sectionId = 10;
                $entry->enabled = true;
                $entry->title = "Review ID".$id;
                $entry->setFieldValues([
                    'reviewId' => $id
                    'reviewText' => $text,
                ]);
                $success = Craft::$app->elements->saveElement($entry);
                if (!$success) {
                      Craft::error('Couldn’t save the review ID"'.$id.'"', __METHOD__);
                }
            }

        }
        return "Ready";
    }
}

That's what my code does - it receives a list of reviews from a third-party service, runs through the reviews that are in my database. If the code finds some new review, it adds it to the database.

Checking for a new review will be carried out by the created simple field "reviewId". At least that's how it should work. And it works, but not when it needs to.

In the file routes.php I made CRAFT systems so that my module worked out by the URL of my site: site.com/reviews/update . At the hoster in CRON, I set a timer for processing an HTTP request (1 time per hour should be executed).

And here something strange happens.

If I personally open my URL in the browser, then everything works out as intended! But for some reason CRON reacts strangely to if(count($entries) == 0). A new review is added to the database, but when the CRON task is run again for the condition if(count($entries) == 0) for the same review returns TRUE. As if this new comment was not added to the database in the previous task. But this is not the case, it is added to the database normally. In this case, my database is filled with a bunch of identical comments in the morning. What could be the problem? Help plz

3
  • What does the cron job command look like? Commented Nov 5, 2022 at 19:02
  • Oh, I don't know. In the hosting control panel, it looks like a list of tasks that can be performed on a timer. I have already selected "execute HTTP request once per hour"
    – Dimi
    Commented Nov 5, 2022 at 19:19
  • 1
    I also noticed such a nuance. If I am logged in to the system, then everything goes fine according to the cron task. Does not add duplicates. But if I go to sleep, for example, I see duplicates in the morning.
    – Dimi
    Commented Nov 5, 2022 at 19:22

1 Answer 1

1

This probably happens because you're using the .search() param to query for the existing review entry, which means that Craft will look in the search index to try and find an entry matching the review ID.

The problem is that the search index isn't necessarily always up to date with the latest entry data, since Craft updates the search index asynchronously via a queue job. In general, you should never rely on the search index for mission critical logic like this.

To fix the issue, query by the reviewId field directly instead of using .search(), i.e.:

$entries = Entry::find()->section('reviews')->reviewId($id)->all();
5
  • 1
    As I understand it, using search() also significantly reduces the speed of processing my code. The error has been fixed + the code has become faster to work out. Thanks man)
    – Dimi
    Commented Nov 6, 2022 at 1:14
  • Yeah, .search() is comparatively slow compared to querying on field values directly. Use it for actual searches only :) Commented Nov 6, 2022 at 1:47
  • @Dimi Also, re: performance you might consider using .exists() instead of .all(), which is a much cheaper query – i.e. $reviewExists = Entry::find()->section('reviews')->search('reviewId::'.$id)->exists(); ... if (!$reviewExists) { ... Commented Nov 6, 2022 at 1:51
  • Yes, this construction i think best solution: $reviewExists = Entry::find()->section('reviews')->reviewId($id)->exists(); I'm just learning the system. Your advice is very helpful. Thanks again
    – Dimi
    Commented Nov 9, 2022 at 13:35
  • By the way, I created a separate question about the catalog filter. I think that there just need to use search
    – Dimi
    Commented Nov 9, 2022 at 15:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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