I have a problem wanting to pass the id of Products in the subqueries.
The first code is what I have so far. The second is the way I want to do with Eloquent, but I can't.
$result = [];
Product::with(['locals.presentations'])->each(function ($product) use (&$result) {
$body['id'] = $product->id;
$body['nombre'] = $product->nombre;
$sedes = [];
$product->locals->each(function ($local) use (&$sedes, $product) {
$presentations = [];
$local->presentations->each(function ($presentation) use (&$presentations, $local, $product) {
if ($presentation->local_id == $local->id && $presentation->product_id == $product->id) {
$presentations[] = [
'local_id' => $presentation->local_id,
'product_id' => $presentation->product_id,
'presentacion' => $presentation->presentation,
'precio_default' => $presentation->price
];
}
});
...
});
return $result;
I want transform the previous code into this with Eloquent, but I can't pass the product_id into the subqueries:
$products = Product::with(['locals' => function ($locals) {
//How to get the id from Product to pass in the $presentations query ??????
$locals->select('locals.id', 'descripcion')
->with(['presentations' => function ($presentations) {
$presentations
// ->where('presentations.product_id', $product_id?????)
->select(
'presentations.local_id',
'presentations.product_id',
'presentations.id',
'presentation',
'price'
);
}]);
}])->select('products.id', 'nombre')->get();
return $products;
Product
public function locals()
{
return $this->belongsToMany(Local::class)->using(LocalProduct::class)
->withPivot(['id', 'is_active'])
->withTimestamps();
}
Local
public function presentations()
{
return $this->hasManyThrough(
Presentation::class,
LocalProduct::class,
'local_id',
'local_product_id'
);
}
You can simply use the has() method if you have set the relations correctly on the Product and Local models. This will return ONLY the products which has locals AND presentations.
If you want every product but only the locals and presentations with the product_id equals to the products.id, then you don't have to do anything. The relationship you set in your models already checks if the id matches.
$products = Product::has('locals.presentations')
->with(['locals' => function ($locals) {
$locals
->select('locals.id', 'descripcion')
->with(['presentations' => function ($presentations) {
$presentations->select(
'presentations.local_id',
'presentations.product_id',
'presentations.id',
'presentation',
'price'
);
}]);
}])->select('products.id', 'nombre')->get();
Related
When I run the query normally it won't update the status_order from 0 to 1, but when I put a dd() function after the query to check if it will update properly, it will give the same result the first time I run the code, but when I refresh the page it will update to a 1.
Here's how my code usually looks:
public function payment(Request $request){
$total = 0;
$orderInfo = $this->getOrderInfo();
$json2 = array();
foreach($this->getOrderInfo()->products as $product){
$total += ($product->price * $product->pivot->order_quantity);
}
if(Customer::find(Auth::user()->id)->balance >= $total && $orderInfo !== null){
if($orderInfo->order_status !== 1){
$orderInfo->where('customer_id', Auth::user()->id)
->where('order_status', 0)
->update(['order_status' => 1]);
}
foreach($orderInfo->products as $product){
$json = array('order_id' => $product->pivot->order_id,
'product_id' => $product->pivot->product_id,
'product_name' => $product->name,
'price' => $product->price,
'quantity' => $product->pivot->order_quantity);
array_push($json2, $json);
}
Customer::where('id', Auth::user()->id)->decrement('balance', $total);
array_push($json2, array('order_status' => $orderInfo->order_status));
$productInfo = json_encode($json2, JSON_PRETTY_PRINT);
OrderHistory::create([
'customer_id' => Auth::user()->id,
'orderInfo' => $productInfo
]);
$orderInfo->products()
->detach();
$orderInfo->delete();
return back();
}else{
return "Not enough balance";
}
}
}
Here's where I put my dd() function:
if($orderInfo->order_status !== 1){
$orderInfo->where('customer_id', Auth::user()->id)
->where('order_status', 0)
->update(['order_status' => 1]);
dd($orderInfo->where('customer_id', Auth::user()->id)->where('order_status', 0));
}
The if($orderInfo->order_status !== 1) is put in there for me to check if the query would get skipped at all. I have tried to alter the order in which the code is presented, but it didn't make any difference.
this code produce mass update but doesn't affect your $orderInfo model which is loaded when it had order_status == 0
$orderInfo = $this->getOrderInfo();
// ...
$orderInfo->where('customer_id', Auth::user()->id)
->where('order_status', 0)
->update(['order_status' => 1]);
// in database data was updated, but $orderInfo is already in memory, so
// $orderInfo->order_status == 0
in case you want to get immediately impact on $orderInfo try
// if you order can have only one info
$orderInfo->order_status = 1;
$orderInfo->save();
// if order can have multiple info models
$orderInfo->newQuery()->where('customer_id', Auth::user()->id)
->where('order_status', 0)
->update(['order_status' => 1]);
$orderInfo = $orderInfo->fresh();
docs about fresh() method
as a sidenote here you're doing duplicate getOrderInfo() call
$orderInfo = $this->getOrderInfo();
$json2 = array();
//foreach($this->getOrderInfo()->products as $product) {
foreach($orderInfo->products as $product) {
$total += ($product->price * $product->pivot->order_quantity);
}
update to clarify about comment to main post
truth to be told, i'm confused that this code runs at all. i meant
$orderInfo is an object given you checked its property order_status
but then you call where() on it as if it is a collection (or model).
also its not laravel-query-builder but laravel-eloquent or just
eloquent given you have detach() there.. – Bagus Tesa
if we dig into Illuminate\Database\Eloquent\Model class there is 'magic' method __call
/**
* Handle dynamic method calls into the model.
*
* #param string $method
* #param array $parameters
* #return mixed
*/
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
if ($resolver = (static::$relationResolvers[get_class($this)][$method] ?? null)) {
return $resolver($this);
}
// that's why OP code works
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
as you can see if model has no method to call it forwards call to Builder object (result of $this->newQuery()) which is equivalent to ModelName::query()
tbh, i agree that calling eloquent from loaded model is a bit frustrating, but it is 'by design'
In order to do a mass update, what this theoretically is, you need to define all the attributes that you want to mass update in the $fillable array in your Model. (OrderInfo in this case)
My controlller can't read that the 'sample' is a store and not a product.
I have this Route on my web.php
Route::get('{store}/products/{products}/edit', [
'as' => 'store.products.edit',
'uses' => 'ProductsController#edit',
function($store) {
$store = App\Models\Store::where('slug', $store)->firstOrFail();
}
]);
and here is my ProductsController#edit
public function edit($id)
{
$product = Product::findOrFail($id);
return view('view here', compact('product'));
}
when I run the url: http://127.0.0.1:8000/sample/products/022fe902-7f4d-4db1-b562-04a7eb9f5a68/edit
where sample is the {store} and 022fe902-7f4d-4db1-b562-04a7eb9f5a68 is the {product}
I get this error:
No query results for model [App\Models\Product] sample
in my query:
select * from products where products.uuid = 'sample' limit 1
If you have 2 parameters, you should have them both in your controller in valid order, so you should have:
public function edit($store, $id)
{
$product = Product::findOrFail($id);
return view('view here', compact('product'));
}
Also probably you don't need here:
function($store) {
$store = App\Models\Store::where('slug', $store)->firstOrFail();
}
for anything, but maybe in your controller you should do something like this:
$store = App\Models\Store::where('slug', $store)->firstOrFail();
$product = $store->products()->findOrFail($id);
Assuming you have product in this store and you would like to make sure that someone won't edit product that is assigned to different store.
I am using Laravel for controller and blade file for a webpage. My code is something like:
PropertiesController
$properties = Property::where('status', 1);
$properties = $properties->orderBy('properties.created_at', 'DESC')->paginate(8);
return view('properties.index')->with('properties', $properties);
in index.blade.php
#foreach ($properties as $property)
<div class="geo">
<span class="lat">{{ $property->title }}</span>,
<span class="lng">{{ $property->description }}</span>
</div>
what I want to achieve is to get categories w.r.t. counts along with properties, for that, I am doing
$properties = Property::where('status', 1);
$categories = array();
if (is_null($req->c)) {
$search = $properties;
foreach (Category::all() as $category) {
array_push(
$categories,
array(
'id' => $category->id,
'name' => $category->category,
'counts' => count($search->where('properties.category', $category->id)->get()),
)
);
}
}
$properties = $properties->orderBy('properties.created_at', 'DESC')->paginate(8);
return view('properties.index')->with('properties', $properties)->with('categories', $categories);
$search = $properties; and
'counts' => count($search->where('properties.category', $category->id)->get()),
with this it gives me
Trying to get property of non-object
<span class="lat"><?php echo e($property->title); ?></span>,
What I think is you want to pass your data to blade view and get counts of categorized data with each category... For that, you can use duplicated functions to count your data separately. e.g.:
public function properties() {
$properties = Property::where('status', 1);
$categories = array();
foreach (Category::all() as $category) {
$count = $this->count($category->id);
array_push(
$categories,
array(
'id' => $category->id,
'name' => $category->category,
'counts' => $count,
)
);
}
$properties = $properties->orderBy('properties.created_at', 'DESC')->paginate(8);
return view('properties.index')->with('properties', $properties)->with('categories', $categories);
}
public function count($id) {
$count = count(Property::where('category_id', $id)); // or any variable you are using to connect categories table with
return $count;
}
$count = $this->count($category->id);
This is the line which did the trick.
If the relationships are made in the models you should only use with () in this way.
This is how the controller should be.
$properties = Property::where('status', 1)->with('category')->orderBy('properties.created_at', 'DESC')->paginate(8);
return view('properties.index', compact('propierties'));
This will give you the list of Properties next to the assigned category.
But if you need to list the categories and have in each category the properties you must do this.
$categories = Category::with('properties')->paginate(8);
return view('properties.index', compact('categories'));
I am currently working on a modal where a user can insert an excel file. The task of the system is to upload and/or add a new database record if the records are new or identical to what exists in the database. BUT it also needs a delete function for getting rid of those records where the slug column is not identical to the name column.
At the moment I am using Laravel 5.3, and this is my controller as it is now:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
use App\Http\Requests;
use Illuminate\Support\Facades\DB;
use Input;
use Maatwebsite\Excel\Facades\Excel;
class ProductsController extends Controller {
public function importExcel(Request $request) {
if (Input::hasFile('productFile')) {
$path = Input::file('productFile')->getRealPath();
$checkbox = Input::get('productCheckbox');
$data = Excel::load($path, function($reader) {
})->get();
if (!empty($data) && $data->count()) {
foreach ($data as $key => $value) {
$product = Product::all()->where('slug', $value->slug)->first();
$product_false = Product::all()->where('slug', '!=' , 'name')->get();
if ($product_false !== null){
//delete row if slug does not matches name
dd($product_false);
}
The dd above returns all products, so the collection query is not working properly (see below for the raw SQL that I am trying to run in this collection)
if ($product !== null) {
//update row if exist
$product->name = $value->name;
$product->description = $value->description;
$product->price = $value->price;
$product->save();
} else {
//add new row if not exist
$product = new Product;
$product->slug = $value->slug;
$product->name = $value->name;
$product->description = $value->description;
$product->price = $value->price;
$product->save();
}
}
header("Location: /products");
}
}
}
}
This is the Product model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'slug', 'name', 'description', 'price',
];
}
Here is the PHPMyAdmin raw SQL (which works) that I basically am looking for to use in the collection:
SELECT * FROM `products` WHERE `slug` != `name`
I hope someone can help me out from this pit. I have been sailing the waves of the internet for about 12 hours now just to get this done.
~ nitsuJ
Collections, eloquent and query builder are not the same. Collection provide a bunch of methods to work on arrays, rather then on the database or model.
In collection context whereNot() is not available.
but the same function can be achieved through whereNotIn('key', [value])
collect([
[
'name' => 'foo',
'rank' => 2
],[
'name' => 'bar',
'rank' => 3
],[
'name' => 'foobar',
'rank' => 4
],
])->whereNotIn('rank', [4])
same as where rank not in (4)
Change
$product = Product::all()->where('slug', $value->slug)->first();
$product_false = Product::all()->where('slug', '!=' , 'name')->get();
Into
$product = Product::where('slug', $value->slug)->first();
$product_false = Product::where('slug', '!=' , 'name')->get();
Try this
$product = Product::where('slug', $value->slug)->first();
$product_false = Product::whereRaw('slug != name')->get();
Simple where won't work as it compares products.slug with "name"(string).
I managed to solve it.
$data = Excel::load($path, function($reader) {
$importedSlugs = $data->select(array('slug'))->toArray();
//collection of imported slugs
$collectionOfImportedSlugs = collect($importedSlugs)->flatten()->all();
//get all product slugs
$productSlugs = Product::all()->pluck('slug');
//get all different slugs!
$diffSlugsArray = $productSlugs->diff($collectionOfImportedSlugs)->all();
//dd($diffSlugsArray);
foreach ($diffSlugsArray as $diffSlug) {
$product_false = Product::all()->where('slug', $diffSlug)->first();
echo $product_false->slug . 'has been deleted!';
$product_false->delete();
}
})->get();
I'm new to Laravel and at the moment I have a piece of code in a Controller which without the while loop it works, it retrieves my query from the database.
public function dash($id, Request $request) {
$user = JWTAuth::parseToken()->authenticate();
$postdata = $request->except('token');
$q = DB::select('SELECT * FROM maps WHERE user_id = :id', ['id' => $id]);
if($q->num_rows > 0){
$check = true;
$maps = array();
while($row = mysqli_fetch_array($q)) {
$product = array(
'auth' => 1,
'id' => $row['id'],
'url' => $row['url'],
'locationData' => json_decode($row['locationData']),
'userData' => json_decode($row['userData']),
'visible' => $row['visible'],
'thedate' => $row['thedate']
);
array_push($maps, $product);
}
} else {
$check = false;
}
return response()->json($maps);
}
I am trying to loop through the returned data from $q and use json_decode on 2 key/val pairs but I can't even get this done right.
Don't use mysqli to iterate over the results (Laravel doesn't use mysqli). Results coming back from Laravel's query builder are Traversable, so you can simply use a foreach loop:
$q = DB::select('...');
foreach($q as $row) {
// ...
}
Each $row is going to be an object and not an array:
$product = array(
'auth' => 1,
'id' => $row->id,
'url' => $row->url,
'locationData' => json_decode($row->locationData),
'userData' => json_decode($row->userData),
'visible' => $row->visible,
'thedate' => $row->thedate
);
You're not using $postdata in that function so remove it.
Do not use mysqli in Laravel. Use models and/or the DB query functionality built in.
You're passing the wrong thing to mysqli_fetch_array. It's always returning a non-false value and that's why the loop never ends.
Why are you looping over the row data? Just return the query results-- they're already an array. If you want things like 'locationData' and 'userData' to be decoded JSON then use a model with methods to do this stuff for you. Remember, with MVC you should always put anything data related into models.
So a better way to do this is with Laravel models and relationships:
// put this with the rest of your models
// User.php
class User extends Model
{
function maps ()
{
return $this->hasMany ('App\Map');
}
}
// Maps.php
class Map extends Model
{
// you're not using this right now, but in case your view needs to get
// this stuff you can use these functions
function getLocationData ()
{
return json_decode ($this->locationData);
}
function getUserData ()
{
return json_decode ($this->userData);
}
}
// now in your controller:
public function dash ($id, Request $request) {
// $user should now be an instance of the User model
$user = JWTAuth::parseToken()->authenticate();
// don't use raw SQL if at all possible
//$q = DB::select('SELECT * FROM maps WHERE user_id = :id', ['id' => $id]);
// notice that User has a relationship to Maps defined!
// and it's a has-many relationship so maps() returns an array
// of Map models
$maps = $user->maps ();
return response()->json($maps);
}
You can loop over $q using a foreach:
foreach ($q as $row) {
// Do work here
}
See the Laravel docs for more information.