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

AManInYorkshire's avatar

Eloquent custom collection class is API resource

Hi all.

I have created a custom eloquent collection for a model. My model is BettingEvent and my collection is BettingEventCollection.

Here is the collection class:

<?php
namespace App\Collections;

use App\Models\Bookmaker;
use Illuminate\Database\Eloquent\Collection;

class BettingEventCollection extends Collection
{
    public function totalOdds(Bookmaker $bookmaker, ?int $round = 2): ?float
    {
        $totalOdds = $this->collection->reduce(
            fn($c,$i) => $c * $i->odds->where('bookmaker.code','=',$bookmaker->code)->first()->odds_decimal,
            1
        );

        return $round > 0 ? round($totalOdds,$round) : $totalOdds;
    }
}

So, if I have a collection of BettingEvent I'm able to make a single call to totalOdds(), passing in a bookmaker to get the total odds. This works fine, but I would like to use it in an API resource. I have the following resource:

<?php

namespace App\Http\Resources;

use App\Models\Bookmaker;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Facades\Log;

class BetSlipCollection extends ResourceCollection
{
    public static $wrap = 'betting_events';

    public $collects = BettingEvent::class;

    public function __construct($resource, protected Bookmaker $bookmaker, protected int $stake = 1)
    {
        parent::__construct($resource);
    }

    /**
     * Transform the resource collection into an array.
     *
     * @return array<int|string, mixed>
     */
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }

    public function with(Request $request) {
        return [
            'stake' => $this->stake,
            'count' => $this->count(),
            'total_odds' => $this->collection->totalOdds($this->bookmaker,2),
        ];
    }
}

However, the $this->collection->totalOdds($this->bookmaker,2) call in this class doesn't work, as $this->collection is an instance of Illuminate\Support\Collection , not App\Collections\BettingEventCollection.

I suppose in my API resource constructor, I could capture the resource BettingEventCollection resource I've passed it, but wonder if I'm missing something.

TIA

:wq

0 likes
2 replies
tisuchi's avatar

@amaninyorkshire Try by updating your __construct method like this way:


public function __construct($resource, protected Bookmaker $bookmaker, protected int $stake = 1)
    {
        // Convert to custom collection if it's not already
        if (!$resource instanceof BettingEventCollection) {
            $resource = new BettingEventCollection($resource);
        }
        parent::__construct($resource);
    }
AManInYorkshire's avatar

@tisuchi thanks for the response. I am confident I am passing in the correct collection type. If I put some logging in like I've just done:

    public function __construct($resource, protected Bookmaker $bookmaker, protected int $stake = 1)
    {
        Log::debug("Resouce is ".get_class($resource));

        parent::__construct($resource);

        Log::debug("Collection is ".get_class($this->collection));
    }

I see the following in my logs:

[2024-01-10T10:25:13.918218+00:00] DEBUG: Resouce is App\Collections\BettingEventCollection
[2024-01-10T10:25:13.919467+00:00] DEBUG: Collection is Illuminate\Support\Collection

TIA

:wq

Please or to participate in this conversation.