Joining multiple tables to get an collection of stock items - mysql

I'm currently trying to join 5 tables to retrieve a list of ordered items. Is it possible to have all the items together rather than having multiple collections of 'ordered_packs' with a different ItemID?
My code
if(!\App\OrderedPack::where('orderID', '=', $orderID)->get()->isEmpty()) {
$packs = \App\OrderedPack::where('ordered_packs.orderID', '=', $orderID)
->join('packs', 'ordered_packs.packID', '=', 'packs.packID')
->join('pack_items', 'packs.packID', '=', 'pack_items.packID')
->join('items', 'pack_items.itemID', '=', 'items.itemID')->get();
my returned result
Collection {#288 ▼
#items: array:2 [▼
0 => OrderedPack {#289 ▼
#primaryKey: "ordered_packID"
#table: "ordered_packs"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:30 [▼
"ordered_packID" => 1
"orderID" => 80
"packID" => 7
"pack_quantity" => 12
"created_at" => "2016-02-11 14:20:29"
"updated_at" => "2016-03-08 16:21:02"
"pack_name" => "niceee"
"pack_description" => ""
"pack_itemID" => 5
"itemID" => 9
"item_code" => "INT T-C 12/15"
"companyID" => 1
"item_name" => "International Terms and Conditions"
"initial_level" => 5000
"current_level" => 4000
"item_typeID" => 6
"productID" => 1
"image_location" => "9.pdf"
"box_total" => 100
"offsite_level" => 1000
"locationID" => 1
"language" => "English"
"download_url" => ""
"archived" => 0
"low_stock_level" => 1000
"change_description" => ""
"old_itemID" => 0
"pdf_only" => 0
"total_stock" => 5000
"groupID" => 2
]
#original: array:30 [▶]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [▶]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
}
1 => OrderedPack {#290 ▼
#primaryKey: "ordered_packID"
#table: "ordered_packs"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:30 [▼
"ordered_packID" => 1
"orderID" => 80
"packID" => 7
"pack_quantity" => 12
"created_at" => "2016-02-11 14:18:35"
"updated_at" => "2016-03-08 16:21:02"
"pack_name" => "niceee"
"pack_description" => ""
"pack_itemID" => 6
"itemID" => 7
"item_code" => "GW CHO INT BRO 12/15"
"companyID" => 1
"item_name" => "GW Choice International Brochure (Including Details Guide) 12/15"
"initial_level" => 5000
"current_level" => 234
"item_typeID" => 1
"productID" => 2
"image_location" => "7.pdf"
"box_total" => 100
"offsite_level" => 2304
"locationID" => 1
"language" => "English"
"download_url" => ""
"archived" => 0
"low_stock_level" => 1000
"change_description" => ""
"old_itemID" => 0
"pdf_only" => 0
"total_stock" => 2538
"groupID" => 2
]
I have an orderedpack instance for every item in a pack. I would like one ordered pack instance that contains multiple items.
The tables
order->ordered_packs->packs->pack_items->items

In one query is not possible to take advantage of Laravel, but you could take advantage of eager loading:
$packs = \App\OrderedPack::where('ordered_packs.orderID', '=', $orderID)
->with('packs.pack_items.items')
->get();
Assuming your relationships are setup correctly

Related

How do I join multiple tables and use data from one table to make columns for the output?

I am building an error tracking system using Laravel and MySQL so far I have 3 tables
fillers as f: Holds the fillers id, first, and last name columns:
id,= el.tech,
first_name,
last_name
error_type as et: Holds a table to describe the types of possible errors and assigns an id columns:
id,
name = el.error_id
error_logs as el: Holds all errors for all fillers columns:
id,
error_id, =et.name
tech, =f.id
public function countAllLogs()
{
$errorLog = DB::table('fillers AS f')
->select(
'f.id as id',
'f.first_name as first_name',
'f.last_name as last_name',
// 'f.location as location',
'et.name as error_type',
)
->selectRaw(
'count(el.error_id) as count',
)
->groupBy('f.id')
->groupBy('el.error_id')
->leftjoin('error_logs as el', 'f.id', '=', 'el.tech')
->leftjoin('error_types as et', 'et.id', '=', 'el.error_id')
->get();
return response($errorLog, 200);
}
using Postman Get function gives the following example
{
"id": 59,
"first_name": "Steve",
"last_name": "Martian",
"error_type": "ndc",
"count": 3
},
{
"id": 59,
"first_name": "Steve",
"last_name": "Martian",
"error_type": "jumper",
"count": 1
}
That is almost what I want but it separates out based on the "error_type." I have seen "pivot" but am unsure how to implement this with what I have.
The desired output would be
{
"id": 59,
"first_name": "Steve",
"last_name": "Martian",
"jumper": 1,
"ndc": 3
}
Thanks.
You can achieve this with the flatMap collection method. I'm assuming multiple ids will be present, so groupBy and map will also be used.
$errorLog = DB::table('fillers AS f')->...->get();
$json = $errorLog
->groupBy('id')
->map->flatMap(fn($item) => [
'id' => $item->id,
'first_name' => $item->first_name,
'last_name' => $item->last_name,
$item->error_type => $item->count
])
->values()
->toJson();
return response($json, 200);
Explanation
Here's the original collection. I've added a couple of entries to demonstrate different ids.
array:4 [
0 => {#4540
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
1 => {#4567
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
2 => {#4569
+"id": 57
+"first_name": "qwer"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
3 => {#4573
+"id": 58
+"first_name": "asdf"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
]
groupBy('id'): Groups the collection by id. This is important for the next step.
array:3 [
59 => Illuminate\Support\Collection {#4592
#items: array:2 [
0 => {#4540
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
1 => {#4567
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
]
}
57 => Illuminate\Support\Collection {#4557
#items: array:1 [
0 => {#4569
+"id": 57
+"first_name": "qwer"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
]
}
58 => Illuminate\Support\Collection {#4591
#items: array:1 [
0 => {#4573
+"id": 58
+"first_name": "asdf"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
]
}
]
flatMap(Closure): maps the collection and then collapses it. Same as map(Closure)->collapse().
map->flatMap(fn($item) => [...]): Shorthand for
map(function ($grouped) {
return $grouped->flatMap(function ($item) {
return [...];
});
});
This notation is possible thanks to high-order collection methods.
If the groupBy step was not done, then you'd end up with a single item, data completely mixed together.
Here's how the Collection looks like after map->flatMap(Closure):
array:3 [
59 => Illuminate\Support\Collection {#4590
#items: array:5 [
"id" => 59
"first_name" => "Steve"
"last_name" => "Martian"
"ndc" => 3
"jumper" => 1
]
}
57 => Illuminate\Support\Collection {#4570
#items: array:4 [
"id" => 57
"first_name" => "qwer"
"last_name" => "Martian"
"ndc" => 3
]
}
58 => Illuminate\Support\Collection {#4588
#items: array:4 [
"id" => 58
"first_name" => "asdf"
"last_name" => "Martian"
"jumper" => 1
]
}
]
values(): Discard the array keys. This is important because toJson() would produce a json object instead of a json array if it tried to conserve the keys.
You can see that for yourself with a simple experiment: Compare the results between
json_encode([ ['a' => 2, 'b' => 3] ]);
json_encode([1 => ['a' => 2, 'b' => 3] ]);
array:3 [
0 => Illuminate\Support\Collection {#4590
#items: array:5 [
"id" => 59
"first_name" => "Steve"
"last_name" => "Martian"
"ndc" => 3
"jumper" => 1
]
}
1 => Illuminate\Support\Collection {#4570
#items: array:4 [
"id" => 57
"first_name" => "qwer"
"last_name" => "Martian"
"ndc" => 3
]
}
2 => Illuminate\Support\Collection {#4588
#items: array:4 [
"id" => 58
"first_name" => "asdf"
"last_name" => "Martian"
"jumper" => 1
]
}
]
toJson(): Transforms collection into json string: (I've added line breaks for better readability)
[
{
"id":59,
"first_name":"Steve",
"last_name":"Martian",
"ndc":3,
"jumper":1
},
{
"id":57,
"first_name":"qwer",
"last_name":"Martian",
"ndc":3
},
{
"id":58,
"first_name":"asdf",
"last_name":"Martian",
"jumper":1
}
]

Kartik-Widget GridView Filter TYPEAHEAD inactive

Following code throws out no error,but it's completely inactive, respectively redundant. JQuery is filtering nothing! Any ideas, how to fix this?
Here is code:
[
'attribute' => 'name',
'label' => 'Land',
'value' => function($model) {
if ($model->name) {
return $model->name;
} else {
return NULL;
}
},
'filterType' => GridView::FILTER_TYPEAHEAD,
'filterWidgetOptions' => [
'pluginOptions' => ['highlight' => true],
//'dataset' => [['local' => array_values(\app\models\Country::find()->orderBy('name')->asArray()->one())]
'dataset' => [['local' => array_values(ArrayHelper::map(\app\models\Country::find()->all(), 'id', 'name'))]
]],
'filterInputOptions' => ['placeholder' => 'JQuery will filter...'],
'format' => 'raw'
],
Here is var_dump of
$ausgabe_ = array(array_values(ArrayHelper::map(\app\models\Country::find()->all(), 'id', 'name')));
E:\xampp\htdocs\Yii-WSL\views\country\index.php:145:
array (size=1)
0 =>
array (size=25)
0 => string 'Arabische Emirate' (length=17)
1 => string 'Algerien' (length=8)
2 => string 'Australia' (length=9)
3 => string 'Belgien' (length=7)
4 => string 'Brasilien' (length=9)
5 => string 'Canada' (length=6)
6 => string 'Schweiz' (length=7)
7 => string 'China' (length=5)
8 => string 'Zypern' (length=6)
9 => string 'Germany' (length=7)
10 => string 'Westsahara' (length=10)
11 => string 'France' (length=6)
12 => string 'United Kingdom' (length=14)
13 => string 'Ungarn' (length=6)
14 => string 'India' (length=5)
15 => string 'Laos' (length=4)
16 => string 'Russia' (length=6)
17 => string 'Sudan' (length=5)
18 => string 'Turkmenistan' (length=12)
19 => string 'Ukraine' (length=7)
20 => string 'Uganda' (length=6)
21 => string 'United States' (length=13)
22 => string 'Vatikanstadt' (length=12)
23 => string 'Vietnam' (length=7)
24 => string 'Südafrika' (length=10)
Any further ideas,how to fix this?
P.S.: If I try like this.....:
'dataset' => [
['local' => array_values([ArrayHelper::map(\app\models\Country::find()->orderBy('name')->asArray()->all(), 'id', 'name ')])],
]
....result of
$ausgabe_ = array(array_values(ArrayHelper::map(Country::find()->orderBy('name')->asArray()->all(), 'id', 'name ')));
var_dump($ausgabe_);
is like this:
E:\xampp\htdocs\Yii-WSL\views\country\index.php:146:
array (size=1)
0 =>
array (size=25)
0 => null
1 => null
2 => null
3 => null
4 => null
5 => null
6 => null
7 => null
8 => null
9 => null
10 => null
11 => null
12 => null
13 => null
14 => null
15 => null
16 => null
17 => null
18 => null
19 => null
20 => null
21 => null
22 => null
23 => null
24 => null
Nothing helps to fix this problem. Further ideas?
Worth to note the array must be with integer type indexes and there cannot be missing numbers (indexes) (for example, 0; 1; 2; 3 is fine but 0; 1; 3; 4 is not because index 2 is missing).
The only valid structure (as example):
array(4) {
[0]=>
string(5) "Alpha"
[1]=>
string(4) "Beta"
[2]=>
string(5) "Gamma"
[3]=>
string(5) "Delta"
}
Your array is not valid:
The first (and only) element is 0 that contains other array. It cannot be like that;
That larger array has mixed indexes (read requirement in the first paragraph);
null values are not accepted.
What might solve your problem is if you use array_values:
'dataset' => [
['local' => array_values([ArrayHelper::map(\app\models\Country::find()->orderBy('name')->asArray()->all(), 'id', 'name ')])],
],

Getting multiple rows of same row with just one column change from database

I'm using following query to fetch record from database. It has join with to many other tables. Everything work fine but problem is I'm getting multiple rows of same reservation and it's because reservation table has join with payment table and payment table has multiple rows against one reservation that's why it repeat complete record. What I want is to show single row having all payments rows in it so that I can loop through all payments and display them instead of displaying entire reservation record again and again. below is my query. It's Laravel 5.2.
return $results = DB::table('resorts_reservation')
->join('resorts_resort', 'resorts_reservation.resortId', '=', 'resorts_resort.resortId')
->join('resorts_roomtype', 'resorts_reservation.roomTypeId', '=', 'resorts_roomtype.roomTypeId')
->join('resorts_customer', 'resorts_reservation.customerId', '=', 'resorts_customer.customerId')
->join('resorts_salesperson', 'resorts_reservation.salesPersonId', '=', 'resorts_salesperson.salesPersonId')
->join('resorts_payment', 'resorts_reservation.reservationId', '=', 'resorts_payment.reservationId')
->join('resorts_paymentmethod', 'resorts_payment.paymentMethodId', '=', 'resorts_paymentmethod.paymentMethodId')
->join('resorts_emailnotification', 'resorts_reservation.reservationId', '=', 'resorts_emailnotification.reservationId')
->join('resorts_resortcompany', 'resorts_resort.resortCompanyId', '=', 'resorts_resortcompany.resortCompanyId')
->select('resorts_reservation.totalAmount', 'resorts_reservation.saleDate', 'resorts_reservation.reservationId',
'resorts_reservation.confirmNo', 'resorts_reservation.numberOfNights', 'resorts_reservation.checkInDate',
'resorts_reservation.checkOutDate', 'resorts_reservation.numberOfAdults', 'resorts_reservation.numberOfChildren',
'resorts_reservation.totalInParty', 'resorts_reservation.notes', 'resorts_reservation.totalPrice',
'resorts_reservation.saleSource', 'resorts_reservation.depositAmount', 'resorts_reservation.confirmationSent',
'resorts_reservation.finalized',
'resorts_customer.firstName', 'resorts_customer.mobilePhone', 'resorts_customer.otherPhone',
'resorts_customer.email', 'resorts_customer.addressLineOne', 'resorts_customer.city',
'resorts_customer.country', 'resorts_customer.state_', 'resorts_customer.postalCode',
'resorts_resort.resortName',
'resorts_roomtype.roomTypeDesc', 'resorts_roomtype.occupancy',
'resorts_salesperson.firstName as saleFirstName','resorts_salesperson.lastName as saleLastName',
'resorts_paymentmethod.methodType',
'resorts_payment.transactionNo',
'resorts_resortcompany.resortCompanyName')
->whereRaw($where)
->orderBy('reservationId', 'desc')
->get();
Below is the result return from the query. You can see both rows has same record with just "transactionNo" column change.
[0] => stdClass Object
(
[totalAmount] => 2161.2
[saleDate] => 2016-03-09 00:00:00
[reservationId] => 30286
[confirmNo] =>
[numberOfNights] => 3
[checkInDate] => 2016-04-22 00:00:00
[checkOutDate] => 2016-04-25 00:00:00
[numberOfAdults] => 6
[numberOfChildren] => 0
[totalInParty] => 6
[notes] =>
[totalPrice] => 2161.2
[saleSource] => VRBO
[depositAmount] => 2161.2
[confirmationSent] => 0
[finalized] => 0
[firstName] => Michael
[mobilePhone] => 505-321-2106
[otherPhone] =>
[email] => xxxxxxxx#gmail.com
[addressLineOne] =>
[city] => Albuquerque
[country] => USA
[state_] => NM
[postalCode] => 87111
[resortName] => San Francisco - Canterbury
[roomTypeDesc] => 3 Bedroom Presidential
[occupancy] => 6
[saleFirstName] => Kristy
[saleLastName] => Conlin
[methodType] => CREDIT_CARD
[transactionNo] => 7MG983973K453254C
[resortCompanyName] => Wyndham
)
[1] => stdClass Object
(
[totalAmount] => 2161.2
[saleDate] => 2016-03-09 00:00:00
[reservationId] => 30286
[confirmNo] =>
[numberOfNights] => 3
[checkInDate] => 2016-04-22 00:00:00
[checkOutDate] => 2016-04-25 00:00:00
[numberOfAdults] => 6
[numberOfChildren] => 0
[totalInParty] => 6
[notes] =>
[totalPrice] => 2161.2
[saleSource] => VRBO
[depositAmount] => 2161.2
[confirmationSent] => 0
[finalized] => 0
[firstName] => Michael
[mobilePhone] => 505-321-2106
[otherPhone] =>
[email] => xxxxxxxx#gmail.com
[addressLineOne] =>
[city] => Albuquerque
[country] => USA
[state_] => NM
[postalCode] => 87111
[resortName] => San Francisco - Canterbury
[roomTypeDesc] => 3 Bedroom Presidential
[occupancy] => 6
[saleFirstName] => Kristy
[saleLastName] => Conlin
[methodType] => CREDIT_CARD
[transactionNo] =>
[resortCompanyName] => Wyndham
)
What I want is something like below:
[0] => stdClass Object
(
[totalAmount] => 2161.2
[saleDate] => 2016-03-09 00:00:00
[reservationId] => 30286
[confirmNo] =>
[numberOfNights] => 3
[checkInDate] => 2016-04-22 00:00:00
[checkOutDate] => 2016-04-25 00:00:00
[numberOfAdults] => 6
[numberOfChildren] => 0
[totalInParty] => 6
[notes] =>
[totalPrice] => 2161.2
[saleSource] => VRBO
[depositAmount] => 2161.2
[confirmationSent] => 0
[finalized] => 0
[firstName] => Michael
[mobilePhone] => 505-321-2106
[otherPhone] =>
[email] => xxxxxxxx#gmail.com
[addressLineOne] =>
[city] => Albuquerque
[country] => USA
[state_] => NM
[postalCode] => 87111
[resortName] => San Francisco - Canterbury
[roomTypeDesc] => 3 Bedroom Presidential
[occupancy] => 6
[saleFirstName] => Kristy
[saleLastName] => Conlin
[methodType] => CREDIT_CARD
array(
[transactionNo] => 7MG983973K453254C
[transactionNo] =>
)
[resortCompanyName] => Wyndham
)
You can use GROUP-CONCAT function to group all payment transaction numbers for each reservation.
From MySQL docs
This function returns a string result with the concatenated non-NULL
values from a group. It returns NULL if there are no non-NULL values.
Your query will look something like this:
SELECT resorts_reservation.totalAmount, ..., GROUP_CONCAT(resorts_payment.transactionNo separator ', ') as transactionNoList
WHERE...
Hope this makes sense, if not, I will be glad to clarify.
Tom Rushman

Laravel - Eloquent overwriting a custom timestamp... WHY?

I am making an inventory management system.
When a product is out of stock, I make an entry in a table and note the "oos_at" field with the date/time.
later, when it is back in stock, i find that entry and update the "restocked_at" timestamp field.
However, when I do the second action, my "oos_at" field is overwritten with the timestamp of the query ('updated_at') field...
I had to keep track of this "oos_at" field on another entity so it wouldn't get overwritten and then use that field to update the "oos_at" field a second time when updating that table...
Below is my code....
class Pusher extends Model {
/**
*
*
* #param Carbon $oos_at
* #return bool
*/
public function setAsOutOfStock(Carbon $oos_at)
{
Log::info([
'FILE' => get_class($this),
'method' => 'setAsOutOfStock',
'pusher' => $this->id,
'param:oos_at' => $oos_at->toDateTimeString(),
]);
$this->pusherOutOfStocks()->create([
'location_id' => $this->location_id,
'product_id' => $this->product_id,
'oos_at' => $oos_at->toDateTimeString(),
]);
$this->oos = true;
$this->oos_at = $oos_at;
return $this->save();
}
/**
* Clear the PusherOutOfStocks attached to $this Pusher
* (This Pusher has been restocked!)
*
* #param Carbon $time
* #return bool
*/
public function setAsInStock(Carbon $time)
{
Log::info([
'FILE' => get_class($this),
'method' => 'setAsInStock',
'pusher' => $this->id,
'param:time' => $time->toDateTimeString(),
]);
$this->pusherOutOfStocks()->where('restocked_at', null)
->update(['restocked_at' => $time->toDateTimeString()]);
$this->oos = false;
$this->oos_at = null;
return $this->save();
}
}
When I die and dump the PusherOutOfStocks BEFORE the pusher is restocked, then the "oos_at" is set appropriately.
Illuminate\Database\Eloquent\Collection {#340
#items: array:1 [
0 => Newave\Engineering\PusherOutOfStock {#343
#fillable: array:5 [
0 => "pusher_id"
1 => "location_id"
2 => "product_id"
3 => "oos_at"
4 => "restocked_at"
]
#dates: array:2 [
0 => "oos_at"
1 => "restocked_at"
]
#connection: null
#table: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:9 [
"id" => 246
"pusher_id" => 216
"location_id" => 634
"product_id" => 378
"oos_at" => "2016-03-11 03:00:00"
"restocked_at" => null
"created_at" => "2016-03-11 12:12:01"
"updated_at" => "2016-03-11 12:12:01"
"deleted_at" => null
]
#original: array:9 [
"id" => 246
"pusher_id" => 216
"location_id" => 634
"product_id" => 378
"oos_at" => "2016-03-11 03:00:00"
"restocked_at" => null
"created_at" => "2016-03-11 12:12:01"
"updated_at" => "2016-03-11 12:12:01"
"deleted_at" => null
]
#relations: []
#hidden: []
#visible: []
#appends: []
#guarded: array:1 [
0 => "*"
]
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
#forceDeleting: false
}
]
}
When I die and dump the PusherOutOfStocks AFTER the pusher is restocked, then the "oos_at" is the same as "updated_at"
Illuminate\Database\Eloquent\Collection {#408
#items: array:1 [
0 => Newave\Engineering\PusherOutOfStock {#775
#fillable: array:5 [
0 => "pusher_id"
1 => "location_id"
2 => "product_id"
3 => "oos_at"
4 => "restocked_at"
]
#dates: array:2 [
0 => "oos_at"
1 => "restocked_at"
]
#connection: null
#table: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:9 [
"id" => 244
"pusher_id" => 214
"location_id" => 626
"product_id" => 374
"oos_at" => "2016-03-11 12:10:23"
"restocked_at" => "2016-03-11 04:00:00"
"created_at" => "2016-03-11 12:10:22"
"updated_at" => "2016-03-11 12:10:23"
"deleted_at" => null
]
#original: array:9 [
"id" => 244
"pusher_id" => 214
"location_id" => 626
"product_id" => 374
"oos_at" => "2016-03-11 12:10:23"
"restocked_at" => "2016-03-11 04:00:00"
"created_at" => "2016-03-11 12:10:22"
"updated_at" => "2016-03-11 12:10:23"
"deleted_at" => null
]
#relations: []
#hidden: []
#visible: []
#appends: []
#guarded: array:1 [
0 => "*"
]
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
#forceDeleting: false
}
]
}
I have even used DB::getQueryLog() and saw NOWHERE that 'oos_at' was being explicitly set/updated....
Nowhere else in my code is this table modified...
Does anyone understand what is happening here????
Thank you!!
=============
Extra Code Snippets
Table Migration::
Schema::create('pusher_out_of_stocks', function (Blueprint $table) {
$table->increments('id');
$table->integer('pusher_id');
$table->integer('location_id');
$table->integer('product_id');
$table->timestamp('oos_at');
$table->timestamp('restocked_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
PusherOutOfStock Class
class PusherOutOfStock extends Model
{
use SoftDeletes;
protected $fillable = [
'pusher_id',
'location_id',
'product_id',
'oos_at',
'restocked_at',
];
protected $dates = [
'oos_at',
'restocked_at'
];
/**
* A PusherOutOfStock belongsTo a Product
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function product()
{
return $this->belongsTo(Product::class);
}
/**
* A PusherOutOfStock belongsTo a Pusher
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function pusher()
{
return $this->belongsTo(Pusher::class);
}
/**
* A PusherOutOfStock belongsTo a Location
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function location()
{
return $this->belongsTo(Location::class);
}
}
Below is the sequence the code goes through to hit that update method
First, it hits my InventoryController in my API namespace
public function upload(Request $request)
{
$data = $request->all();
$header = $this->getHeader($data);
$body = $this->getBody($data);
$reader = $this->getReaderFromHeader($header);
if ( ! $this->guardAgainstNoReader($reader))
return (new Response("Reader with mac-address: ". $header['reader_mac'] . " does not exist.", 409));
$result = $this->inventoryRepository->uploadPusherData($reader, $header, $body);
return $result;
}
Then it sends the request to the InventoryRepository
public function uploadPusherData(Reader $reader, $header, $body)
{
foreach ($body as $pusherData)
{
$result[] = $this->processPusherData($pusher, $header['timestamp'], $pusherData);
}
return $result;
}
Then, inside the repository, it processes one line at a time (in my test, there is only one line)
private function processPusherData(Pusher $pusher, $timestamp, $pusherData)
{
$latestInventory = $pusher->latestInventory;
if (! $latestInventory)
return $this->updatePusher($pusher, $timestamp, $pusherData);
if ($latestInventory->tags_blocked == $pusherData['data_TAGSBLKED'])
return $this->noChangeRecorded($pusher, $latestInventory, $timestamp);
return $this->updatePusher($pusher, $timestamp, $pusherData);
}
Then the pusher is updated...
public function updatePusher($pusher, $timestamp, $pusherData)
{
// See if there are any existing already
$prevInv = $pusher->latestInventory;
// Create the new data
$inventory = Inventory::create([
'pusher_id' => $pusher->id,
'product_id' => $pusher->product_id,
'reader_id' => $pusher->reader->id,
'tags_blocked' => $pusherData['data_TAGSBLKED'],
'paddle_exposed'=> $pusherData['paddle_exposed'],
'created_at' => Carbon::createFromTimestamp($timestamp)
->toDateTimeString(),
]);
if ( !$prevInv || $prevInv->id == $inventory->id )
{
return "first-data" . $timestamp;
}
return $this->checkForEvents($inventory, $prevInv);
}
We check if any events should be triggered... In this case, previous inventory had 9 items in stock... now there are 0.
private function checkForEvents(Inventory $currentInventory, Inventory $previousInventory)
{
if ( ! $previousInventory->oos && $currentInventory->oos && $previousInventory->pusher->oos_notified == 0)
{
$currentInventory->pusher->oos_notified = true;
$currentInventory->pusher->save();
return Event::fire(new InventoryOutOfStock($currentInventory));
}
if ( ( $previousInventory->oos || $previousInventory->status == "RESTOCK" )
&& $currentInventory->tags_blocked > 2 )
{
return Event::fire(new PusherWasRestocked($currentInventory));
}
if ( $currentInventory->status == "RESTOCK" && $previousInventory->pusher->low_stock_notified == 0)
{
$currentInventory->pusher->low_stock_notified = true;
$currentInventory->pusher->save();
return Event::fire(new LowStockAlert($currentInventory));
}
return "no-events";
}
This then fires the event InventoryOutOfStock
That triggers 3 events... 2 are related to notifications being sent etc..
'App\Events\InventoryOutOfStock' => [
'App\Listeners\InventoryOutOfStockUpdater',
'App\Listeners\EmailInventoryOutOfStockNotification',
'App\Listeners\SMSInventoryOutOfStockNotification',
// 'App\Listeners\OutOfStocksUpdater',
],
Which leads us to ...
public function handle(InventoryOutOfStock $event)
{
$pusher = $event->pusher;
$inventory = $event->inventory;
$product = $pusher->product;
$oos = $pusher->setAsOutOfStock($inventory->created_at);
$locationPushers = $product->getPushersByLocation($pusher->location);
$isInStock = false;
foreach ($locationPushers as $pusher)
if ($pusher->oos == 0)
$isInStock = true;
if (! $isInStock)
$product->productOutOfStocks()->create([
'location_id' => $pusher->location_id,
'oos_at' => $event->inventory->created_at,
]);
}
I think that you do not have to use the timestamp method to create your fields, but you should use the dateTime method:
Schema::create('pusher_out_of_stocks', function (Blueprint $table) {
$table->increments('id');
$table->integer('pusher_id');
$table->integer('location_id');
$table->integer('product_id');
$table->dateTime('oos_at');
$table->dateTime('restocked_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
This should work :)
Hope it helped!

Eloquent query results limited by relation criteria

I have two tables, affiliates and meetings and I want to build Eloquent query which will list all affiliates without those who have appointed meeting at specified date and time. So, the result should contain all affiliates who don't have any meeting and also those with meetings but not on specified date and time.
Users: id, name, city...
Meetings: id, client, date, time, user_id...
Can it be done without joins or raw sql (whereRaw or DB::raw) and how?
Test data users:
array:3 [▼
0 => array:5 [▼
"id" => 2
"name" => "John"
"city" => "New York"
"created_at" => "2015-02-19 00:26:43"
"updated_at" => "2015-02-19 00:34:03"
]
1 => array:5 [▼
"id" => 3
"name" => "Elisabeth"
"city" => "Kansas City"
"created_at" => "2015-02-19 00:26:43"
"updated_at" => "2015-02-19 00:26:43"
]
2 => array:5 [▼
"id" => 4
"name" => "Teodora"
"city" => "New York"
"created_at" => "-0001-11-30 00:00:00"
"updated_at" => "-0001-11-30 00:00:00"
]
]
Test data meetings:
array:3 [▼
0 => array:8 [▼
"id" => 1
"client" => "George P."
"date" => "2015-05-15"
"time" => "14:00:00"
"approved" => 1
"user_id" => 2
"created_at" => "2015-02-19 00:26:43"
"updated_at" => "2015-02-19 00:26:43"
]
1 => array:8 [▼
"id" => 2
"client" => "Jack White"
"date" => "2015-05-15"
"time" => "12:00:00"
"approved" => 1
"user_id" => 2
"created_at" => "2015-02-19 00:26:43"
"updated_at" => "2015-02-19 00:26:43"
]
2 => array:8 [▼
"id" => 3
"client" => "Philip B."
"date" => "2015-05-16"
"time" => "16:00:00"
"approved" => 1
"user_id" => 3
"created_at" => "2015-02-19 00:26:43"
"updated_at" => "2015-02-19 00:26:43"
]
]
Expected result all users who don't have meeting on 2015-05-15 at 12:00
array:2 [▼
0 => array:5 [▼
"id" => 3
"name" => "Elisabeth"
"city" => "Kansas City"
"created_at" => "2015-02-19 00:26:43"
"updated_at" => "2015-02-19 00:26:43"
]
1 => array:5 [▼
"id" => 4
"name" => "Teodora"
"city" => "New York"
"created_at" => "-0001-11-30 00:00:00"
"updated_at" => "-0001-11-30 00:00:00"
]
]
Any help appreciated.
Assuming your user model has a relation meetings you can do this:
$users = User::whereDoesntHave('meetings', function($q){
$q->where('time', '12:00:00');
})->get();