Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Atef95's avatar

Handling webhooks from an external service

Hi everyone!

I've this method below which serves as an API that fetchs data from an external service to populate the database.

The external service set up a cron every 3 minutes and calls this method by chunking the payload.. (webhook)

so every 3 minutes they are calling this api 20 times ( for every chunk)..

main method

   public function handleUpsertFeed(UpsertRequest $request)
    {

        $validatedData = $request->validated();

        $feeds = $validatedData['feeds'];
        foreach ($feeds as $feed) {
            $checkIntergation = FeedIntegration::whereIntegrationId($feed['integration_id'])->first();


            if (!$checkIntergation) {
                $this->createUpsertFeed($feed);
            } else {
                $this->updateUpsertFeed($checkIntergation->feed_id, $feed);
            }
        }
        return response()->json([
            'status' => true
        ], 200);
    }

createUpsertFeed method

 public function createUpsertFeed($feed)
    {
        DB::transaction(function () use ($feed) {
            $guid = Uuid::generate()->string;


            // Create external feed
            $externalFeed = ExternalFeed::create([
                'guid' => $guid,
                'internal_alias' => $feed['name'],
                'last_color' => $feed['color'],
                'last_message' => $feed['message'],

            ]);

            $newFeed = Feed::create([
                'name' => $feed['name'],
                'description' => $feed['description'],
                'type' => 'external',
                'dependencies' => (isset($feed['dependencies'])) ? $this->handleDependencies($feed) : '[]',
                'space_id' => $feed['spaceId'],
                'last_reported_status' => $feed['color'],
                'override_color' => $feed['color'],
                'last_reported_message' => $feed['message'],
                'last_state_changed_at' => $feed['updatedAt'],
                'last_updated_at' => Carbon::now()
            ]);
            FeedIntegration::create([
                'feed_id' => $newFeed->id,
                'integration_id' => $feed['integration_id'],
                'external_feed_guid' => $guid
            ]);

            $newFeed->pages()->attach($feed['pageId']);

            $tags = isset($feed['custom_data']['tags']) ? $feed['custom_data']['tags'] : null;


            if ($tags) {

                $tagIds = $this->handleTags($tags);
                $newFeed->tags()->sync($tagIds);
            }
        });
        return true;
    }

updateUpsertFeed method

  public function updateUpsertFeed($feedId, $newFeed)
    {

        $checkFeed = Feed::find($feedId);

        if ($checkFeed) {

            DB::transaction(function () use ($checkFeed, $newFeed) {
                $previousFeedState = new FeedState($checkFeed->last_reported_status, $checkFeed->last_reported_message, $checkFeed->last_state_changed_at);
                $checkFeed->update([
                    'name' => $newFeed['name'],
                    'description' => $newFeed['description'],
                    'space_id' => $newFeed['spaceId'],
                    'dependencies' => (isset($newFeed['dependencies'])) ? $this->handleDependencies($newFeed) : '[]',
                    'last_reported_status' => $newFeed['color'],
                    'override_color' => $newFeed['color'],
                    'last_reported_message' => $newFeed['message'],
                    'last_state_changed_at' => $newFeed['updatedAt'],
                    'last_updated_at' => Carbon::now()
                ]);


                $checkFeed->pages()->sync($newFeed['pageId']);

                $tags = isset($newFeed['custom_data']['tags']) ? $newFeed['custom_data']['tags'] : null;

                if ($tags) {

                    $tagIds = $this->handleTags($tags);

                    $checkFeed->tags()->sync($tagIds);
                }

                $newFeedState = new FeedState($checkFeed->last_reported_status, $checkFeed->last_reported_message, Carbon::now("utc")->format("Y-m-d H:i:s"));
                $feedHasChangedState = $previousFeedState->color != $newFeedState->color ?? true;
                $isNotDefault = $checkFeed->last_reported_status != 'default';
                if ($feedHasChangedState && $isNotDefault) {

                    event(new FeedStateChanged($checkFeed, $previousFeedState, $newFeedState));
                }
            });
        }
        return true;
    }

Helpers methods :

 public function handleDependencies($feed)
    {

        $localDependencies = [];
        if (count($feed['dependencies']) > 0) {
            foreach ($feed['dependencies'] as $dependency) {
                if ($dependency['type'] == 'integration_id') {
                    $checkIntegration = FeedIntegration::whereIntegrationId($dependency['id'])->first();

                    $checkIntegration ? $localDependencies[] = $checkIntegration->feed_id : "";
                }

                if ($dependency['type'] == 'feed_id') {
                    $checkFeed = Feed::find($dependency['id']);

                    $checkFeed ? $localDependencies[] = $checkFeed->id : "";
                }
            }
        }


        return json_encode($localDependencies);
    }

    public function handleTags($tags)
    {
        $ids = [];
        $tagValues = explode(',', $tags);

        foreach ($tagValues as $tag) {
            $newTag = Tag::firstOrCreate(
                ['name' =>  $tag],
                ['name' =>  $tag]
            );
            $ids[] = $newTag->id;
        }
        return $ids;
    }

As you can see many database calls and I'm concerned about performance..

Is handling chunks from my side would be a better approach?

otherwise is throttling the request a good idea too?

I'm eager for your suggestions! Thanks!

0 likes
1 reply
Braunson's avatar

Why not queue the logic, you can fire an event on inbound webhook event, then have various listeners to run in via queue worker.

Please or to participate in this conversation.