How to avoid _matchingData and get a specific value instead - cakephp-3.0

In the Orders Table Model I need the category ids of the products associated with the order
Model/Table/OrdersTable.php
public function updateQuantityForInventoryItems(array $orders_items): void
{
$ordersItemsIds = [];
foreach ($orders_items as $orders_item) {
array_push($ordersItemsIds, $orders_item->product_id);
}
$catIds = $this->find('ordersItemsCategories', ['ordersItemsIds' => $ordersItemsIds])->toArray();
dd($catIds);
$InventoriesTable = TableRegistry::getTableLocator()->get('Inventories');
$InventoriesTable->updateQuantityForInventoryItems('orders', $catIds);
}
public function findOrdersItemsCategories(Query $query, array $options): Query
{
$query = $this->OrdersItems->Products
->find()
->select(['Products.id','CategoriesProducts.category_id'])
->matching(
'CategoriesProducts', function (Query $q) use ($options) {
return $q->where([
'CategoriesProducts.product_id IN' => $options['ordersItemsIds'],
]);
});
return $query->hydrate(false);
}
the generated SQL query
SELECT Products.id AS `Products__id`, CategoriesProducts.category_id AS `CategoriesProducts__category_id` FROM products Products INNER JOIN categories_products CategoriesProducts ON (CategoriesProducts.product_id in (4325,3632) AND Products.id = (CategoriesProducts.product_id))
When I print out the array with dd($catIds); , I get an array with _matchingData
array:4 [▼
0 => array:2 [▼
"id" => 3632
"_matchingData" => array:1 [▼
"CategoriesProducts" => array:1 [▼
"category_id" => 10
]
]
]
1 => ...
]
how do i get category_id under id
(i have already checked that thread How to print _matchingData object value in cakephp 3 but still no success)

got it. i need to alias the category_id to prevent nesting
->select(['category_id' => 'CategoriesProducts.category_id'])

Related

Return string from query Doctrine

I have a query that sums up the total of fields in my database.
//HoursRepository.php
public function findHoursTotal($user)
{
return $this->createQueryBuilder('h')
->where('h.user = :user')
->select('SUM(h.total)')
->setParameter('user', $user)
->getQuery()
->getResult();
}
This query returns:
array:1 [▼
0 => array:1 [▼
1 => "52400"
]
]
My question is how do i get just the "52400" result as a string and not in an array?
you should use getSingleScalarResult(), bellow the usage example :
return $this->createQueryBuilder('h')
->where('h.user = :user')
->select('SUM(h.total)')
->setParameter('user', $user)
->getQuery()
->getSingleScalarResult();

Looping Array of Json to Database Laravel?

I have Json Data like this :
$data = '[
{
"OrderId": "1038806370",
"qtty": "1",
"Item": "Strawberry 250 gr",
"SKU": "20091"
},
{
"OrderId": "1038806370",
"qtty": "2",
"Item": "Strawberry 130 gr",
"SKU": "20092"
},
{
"OrderId": "1038806370",
"qtty": "1",
"Item": "Strawberry 130 gr",
"SKU": "20092"
}
]';
and I want to Save in my database MySql using PHP Laravel,...
I Want To save data from json to my table, there are table Order values [orderID,...] and OrderDetail values [orderID, SKU, Qty]
This is my code in controller
$order = json_decode($data, true);
foreach ($order as $ord) {
$check = Order::where('orderid', $ord['OrderId'])->get();
if (count($data) > 0) {
OrderDetail::firstOrCreate([
'order_id' => $ord['OrderId'],
'sku_id' => $ord['SKU'],
'qty' => $ord['qtty']
]);
} else {
Order::create([
'orderid'=> $ord['OrderId'],
]);
OrderDetail::firstOrCreate([
'order_id' => $ord['OrderId'],
'sku_id' => $ord['SKU'],
'qty' => $ord['qtty']
]);
}
}
but i dont get what i want, I get QTY of SKU 20091 is 2 but actual json data is 3
I'm not sure why you're counting the $data instead of $check variable. You don't really need the $check variable either though. I'll rewrite your code below.
$order = json_decode($data, true);
foreach ($order as $ord) {
// use EXIST query to check if Order exists or not.
if (Order::where('orderid', $ord['OrderId'])->exists())
OrderDetail::firstOrCreate([
'order_id' => $ord['OrderId'],
'sku_id' => $ord['SKU'],
'qty' => $ord['qtty']
]);
} else {
Order::create([
'orderid'=> $ord['OrderId'],
]);
OrderDetail::firstOrCreate([
'order_id' => $ord['OrderId'],
'sku_id' => $ord['SKU'],
'qty' => $ord['qtty']
]);
}
}
But your if/else could also be removed by using the firstOrCreate() method.
$orders = json_decode($data, true);
foreach ($orders as $order) {
// Get Order Model with orderid = $order['OrderId'] or create it if it doesn't exist
$model = Order::firstOrCreate([
'orderid'=> $order['OrderId']
]);
// Get OrderDetail with provided SKU, qtty and orderid or create it if it doesn't exist.
OrderDetail::firstOrCreate([
'order_id' => $model->orderid
'sku_id' => $order['SKU'],
'qty' => $order['qtty']
]);
}
You could also do it inline.
$orders = json_decode($data, true);
foreach ($orders as $order) {
OrderDetail::firstOrCreate([
'order_id' => Order::firstOrCreate(['orderid'=> $order['OrderId']])->orderid,
'sku_id' => $order['SKU'],
'qty' => $order['qtty'],
]);
}

Cakephp3 multiple join table by Model

I have problem with my code. At this moment I have code view like this:
...Table.php
public function containBasic(){
return [
//Keywords
'CandidatesKeywords',
'CandidatesKeywords.Keywords',
//User
'CandidatesUser',
'CandidatesUser.User',
];
}
...Controller.php
$this->loadModel('Candidates');
$candidates = $this->Candidates->find()
->contain($this->Candidates->containBasic())
->where([
'CandidatesKeywords.Keywords.id'=>5
])
->all();
I include all other Models via contain. This method its works but when I try to search data in this code I have error like:
Unknown column 'CandidatesKeyword.id' in 'where clause
I don't know, how I can get column CandidatesKeywords -> Keywords -> Id from database :/
When I get (debug) all rows without where condition I get data like this:
'items' => [
(int) 0 => object(Cake\ORM\Entity) {
'id' => (int) 4,
.........
,
'candidates_user' => null,
'candidates_keyword' => object(App\Model\Entity\CandidatesKeyword) {
'id' => (int) 1,
..................
'keyword' => object(App\Model\Entity\Keyword) {
'id' => (int) 5,
.............
And I need to get rows with only keyword.id = 5.

YII2 creating relations in models between tables from 2 databases

I have defined 2 databases , for example
return [
'components' => [
'db1' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db1name',
'username' => 'db1username',
'password' => 'db1password',
],
'db2' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db2name',
'username' => 'db2username',
'password' => 'db2password',
],
],
];
Now i have a table as 'users' in 'db1' and table 'countries' in 'db2'
users
id , country_code , username , password
1 , DE , xyz , 12345
2 , FR , abc , 12345
countries
code , name
DE , Germany
FR , France
IN , India
I have defined the foreign key relation between users.country_code & countries.code
ISSUE
But when i try to create the model for 'users' table using gii it gives an error , possibly because the tables relation are from 2 different databases.
How to use tables from different databases in relations of a model.
Any suggestions are welcomed
This works in my case to list iten on GridView::widget
-> bd_sisarc is my secound data base
-> deposito_sondagem is a table from my first data base
public static function getDb() // on your model
{
return Yii::$app->get('db1');
}
public static function getDb() // on your model
{
return Yii::$app->get('db2');
}
public function getEmpresaSondagem() // Relation on you model
{
return $this->hasOne(EmpresaSondagem::className(), ['idEmpSondagem' => 'entidade_deposito']);
}
public function search($params)
{
$this->load($params);
$sql = "SELECT deposito_sondagem.*
FROM
deposito_sondagem,
`bd_sisarc`.`tbempresasondagem`
WHERE
`bd_sisarc`.`tbempresasondagem`.`idEmpSondagem`=`deposito_sondagem`.`entidade_deposito`
and deposito_sondagem.estado=1
and tbempresasondagem.estado=1
and numero_registo LIKE '%$this->numero_registo%'
and nomeempsondagem LIKE '%$this->nomeEntidade%'
and dono_sondagem LIKE '%$this->dono_sondagem%'
and data_deposito LIKE '%$this->data_deposito%'";
$query = DepositoSondagem::findBySql($sql);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
Try this one
SELECT `users`.* FROM `users` LEFT JOIN `db2name`.`countries` ON `users`.`country_code` = `db2name`.`countries`.`code `

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!