简体   繁体   中英

NULL Results in Array from Eloquent Query

Does anyone have any idea why this query would be producing some NULL results (array):

$events = $this->event
    ->with('delegates', 'course')
    ->where('start_date', '<=', $today)
    ->where('active', '1')
    ->where('event_status_id', '!=', '3')
    ->hasCosts()
    ->has('delegates')
    ->get()
    ->map(function($event) {
        foreach ($delegates = $event->delegates()->has('contact')->get() as $delegate) {
            $account = $delegate->contact->account;

            return [
                'company' => $account->company_name,
                'income' => $account->income($delegates),
                'profit' => $account->profit($event, $delegates),
                'event' => $account->eventDetails($event, $delegates)
            ];
        }
    })
    ->toArray();

I've stepped into the 'foreach' loop and dumped the id's for each event, account, contact, and delegate and there are no NULL results there. I also receive no errors.

When I dump the $events variable I receive output like:

array(8) {
    [0] NULL
    [1] array(4) {
        ["company"] "Razorfish"
        ["income"] 523
        ["profit"] "69.29"
        ["event"] "ITIL® Service Transition Apr 7, 2014 in London (141)"
    }
    [2] array(4) {
        ["company"] "European Central Bank - Europa ECB"
        ["income"] 1332
        ["profit"] "137.33"
        ["event"] "ITIL® Service Offerings & Agreements Apr 7, 2014 in London (142)"
    }
    [3] array(4) {
        ["company"] "Knowledge Pool - KP delegates"
        ["income"] 475
        ["profit"] "-111.75"
        ["event"] "ITIL® Foundation Apr 7, 2014 in Leeds (143)"
    }
    [4] array(4) {
        ["company"] "Plan International/ Plan UK"
        ["income"] 537
        ["profit"] "118.43"
        ["event"] "ITIL® Foundation Apr 14, 2014 in London (144)"
    }
    [5] array(4) {
        ["company"] "Cell Therapy Catapult ( part of Guy's hospital)"
        ["income"] 550
        ["profit"] "-114.75"
        ["event"] "ITIL® Service Design Apr 14, 2014 in London (145)"
    }
    [6] array(4) {
        ["company"] "European Central Bank - Europa ECB"
        ["income"] 597
        ["profit"] "69.80"
        ["event"] "BCS Specialist Certificate in Supplier Management Apr 14, 2014 in London (146)"
    }
    [7] array(4) {
        ["company"] "C Hoare & Co (hoares bank)"
        ["income"] 523
        ["profit"] "97.71"
        ["event"] "ITIL® Continual Service Improvement Apr 23, 2014 in London (148)"
    }
}

Notice the first NULL result. This is just a sample of the output, but there are numerous results like this in the real output.

For brevity the hasCosts() method in the initial query is a query scope function:

public function scopeHasCosts($query)
{
    return $query->where('tutor_cost', '>', 0)
        ->orWhere('exam_cost', '>', 0)
        ->orWhere('material_cost', '>', 0)
        ->orWhere('venue_cost', '>', 0)
        ->orWhere('notional_cost', '>', 0)
        ->orWhere('buy_price', '>', 0);
}

In your map() callback it looks like you're only returning the first iteration of the foreach loop for delegates with contacts. If an event doesn't have any delegates with contacts, nothing would be returned - hence the null. Is that correct?

As the docs state:

The map method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items

So you're basically replacing each item of the events collection with the array result for the first delegate only. If you wanted the full list of delegates with contacts for each event, it might be better to iterate the events and delegates, building a separate array. Something like this might work:

$events = $this->event
    ->with('delegates', 'course')
    ->where('start_date', '<=', $today)
    ->where('active', '1')
    ->where('event_status_id', '!=', '3')
    ->hasCosts()
    ->has('delegates')
    ->get();

$results = [];

foreach ($events as $event) {
    foreach ($delegates = $events->delegates()->has('contact')->get() as $delegate) {
        $account = $delegate->contact->account;

        $results[] = [
            'company' => $account->company_name,
            'income' => $account->income($delegates),
            'profit' => $account->profit($event, $delegates),
            'event' => $account->eventDetails($event, $delegates)
        ];
    }
}

As @benJ has mentioned, this is partially to do with delegates that don't have contacts.

Each delegate generally has a contact, but if the contact is not known they get linked to an 'unknowns' table which has a unique key and an account associated with it. Sometimes though, the unknowns don't have accounts also.

I have modified the query to do the following:

$events = $this->event
    ->has('delegates')
    ->with('delegates', 'course')
    ->where('start_date', '<=', $today)
    ->where('active', 1)
    ->whereNotIn('event_status_id', [3])
    ->hasCosts()
    ->get()
    ->map(function($event) use ($companies) {
        foreach ($delegates = $event->delegates()->has('contact')->get() as $delegate) {
            $account = $delegate->contact->account;

            return [
                'company' => $account->company_name,
                'account_manager' => $account->user->name(),
                'income' => $account->income($delegates),
                'profit' => $account->profit($event, $delegates),
                'event' => $account->eventDetails($event, $delegates)
            ];
        }

        foreach ($delegates = $event->delegates()->has('unknown')->get() as $delegate) {
            $account = $delegate->unknown->account;

            if ($account) {
                return [
                    'company' => $account->company_name,
                    'account_manager' => $account->user->name(),
                    'income' => $account->income($delegates),
                    'profit' => $account->profit($event, $delegates),
                    'event' => $account->eventDetails($event, $delegates)
                ];
            } else {
                $costPerDelegate = $event->costs() / count($event->delegates);
                $eventVenue = ( ! is_null($event->venue)) ? $event->venue->city : $event->venue_city;
                $eventDetails = ($event->course) ? $event->course->title : 'Unknown Course' . ' ' . $event->start_date->toFormattedDateString() . ' in ' . $eventVenue . ' (' . $event->id . ')';

                return [
                    'company' => 'Unknown',
                    'account_manager' => 'Unknown',
                    'income' => $delegate->price,
                    'profit' => number_format((float) $delegate->price - $costPerDelegate, 2, '.', ''),
                    'event' => $eventDetails
                ];
            }
        }
    })
    ->toArray();

This now returns all results for my query. If anyone has a cleaner way of doing this I would love to hear it.

I now have to work out how to now combine each result based on the company name, then add the income/profits for each company, as well as create a new array under events, listing the events for this company.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM