Flatten laravel nested relationship (parent to descendants) get all childerns - sql

This is my Controller
$categoryIds = Category::select('id')->with('childrenRecursive')->where('id', 1)->get();
Ad::whereIn('category_id', $categoryIds)->get();
This is my model
public function parent() {
return $this->belongsTo(Category::class, 'parent_id');
}
public function childs() {
return $this->hasMany(Category::class, 'parent_id');
}
public function Ads() {
return $this->hasManyThrough(Ad::class, Category::class, 'parent_id', 'category_id', 'id');
}
How get all childern categories ides

I solved this problem with this solution
My Controller
public function index()
{
$parent = Category::with('descendants')->find(1);
$descendants = $this->traverseTree($parent, collect([1]));
$ads = Ad::whereIn('category_id',$descendants)->get();
return response($ads);
}
protected function traverseTree($subtree, $des)
{
$descendants = $des;
if ($subtree->descendants->count() > 0) {
foreach ($subtree->descendants as $descendant) {
$descendants->push($descendant);
$this->traverseTree($descendant, $descendants);
}
}
return $descendants;
}

I'd do it with Laravel's Subqueries approach.
$parentId = 4;
Ad::whereIn('category_id', function($q) use ($parentId) {
$q->select('id')
->from('categories')
->where('parent_id', $parentId);
});
If you want to add the parent model, you can chain with():
Ads::whereIn('category_id', function($q) use ($parentId) {
$q->select('id')
->from('categories')
->where('parent_id', $parentId);
})
->with('category.parent')
->get();
Your code chunks are not clear so you may need to tweak my code example.

If I understand your question properly you need to get ads corresponding to id's of all related records also, for a given category record.
$category = Category::with('childs:id,parent_id')
->where('id', 1)
->firstOrFail();
$categoryIds = collect([$category->parent_id, $category->id]);
$category->childs->map(fn($child) => $categoryIds->push($child->id));
$ads = Ads::whereIn('category_id', $categoryIds->filter()->all())
// Can eager load the product(s) if needed
//->with('products')
->get();

Related

Laravel : I want to calculate total timing for punchin and punch out and generate total time

This is my database I want to calculate time difference between each punch_in and punch_out and calculate all total time for each day.
$punch = punches::where('user_id','=',$user->id )->whereDate('created_at', '=', Carbon::today()->toDateString())->get();
$punch_in = punches::where('user_id','=',$user->id )->whereDate('created_at', '=', Carbon::today()->toDateString())->where('punch_in','!=',null)->get();
$punch_out = punches::where('user_id','=',$user->id )->whereDate('created_at', '=', Carbon::today()->toDateString())->where('punch_out','!=',null)->get();
This is the query that I have been using right now need any suggestion for It
Well you simply use Carbon\Carbon in combination with Accessors for this issue, like this:
Presuming you write something like this in your App\Punch model:
protected $dates = [
'punch_in',
'punch_out'
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function getTimingAttribute(): int
{
if ($this->punch_out) {
return $this->punch_out->diffInSeconds($this->punch_in);
}
return 0;
}
And this then in your App\User model:
public function punches(): HasMany
{
return $this->hasMany(Punch::class);
}
public function getTotalTimingAttribute(): int
{
return $this->punches ? $this->punches->reduce(function ($total, Punch $punch) {
return $total + $punch->timing;
}, 0) : 0;
}
In your App\Http\Controllers\User controller you get it like this:
public function totalTiming(User $user)
{
return $user->totalTiming;
}
I didn't test the code above so it might not work from the get go, but you get the idea.

How to validate two dimensional array in Yii2

How to validate two dimensional array in Yii2.
passenger[0][name] = bell
passenger[0][email] = myemail#test.com
passenger[1][name] = carson123
passenger[1][email] = carson###test.com
how to validate the name and email in this array
Thanks
Probably the most clean solution for validating 2-dimensional array is treating this as array of models. So each array with set of email and name data should be validated separately.
class Passenger extends ActiveRecord {
public function rules() {
return [
[['email', 'name'], 'required'],
[['email'], 'email'],
];
}
}
class PassengersForm extends Model {
/**
* #var Passenger[]
*/
private $passengersModels = [];
public function loadPassengersData($passengersData) {
$this->passengersModels = [];
foreach ($passengersData as $passengerData) {
$model = new Passenger();
$model->setAttributes($passengerData);
$this->passengersModels[] = $model;
}
return !empty($this->passengers);
}
public function validatePassengers() {
foreach ($this->passengersModels as $passenger) {
if (!$passenger->validate()) {
$this->addErrors($passenger->getErrors());
return false;
}
}
return true;
}
}
And in controller:
$model = new PassengersForm();
$model->loadPassengersData(\Yii::$app->request->post('passenger', []));
$isValid = $model->validatePassengers();
You may also use DynamicModel instead of creating Passanger model if you're using it only for validation.
Alternatively you could just create your own validator and use it for each element of array:
public function rules() {
return [
[['passengers'], 'each', 'rule' => [PassengerDataValidator::class]],
];
}
You may also want to read Collecting tabular input section in guide (unfortunately it is still incomplete).

Laravel : How to get the first record of each collections in a 'with' function query

I want to get a collection of all artists with their latest image only.
The following code returns a collection of all artists but with only one image associated with the first artist.
$data = Artist::with(['images' => function($q){
$q->first();
}])
->get();
return $data;
My models:
class Artist extends Model {
public function images()
{
return $this->belongsToMany('App\Image');
}
}
class Image extends Model {
public function artists()
{
return $this->belongsToMany('App\Artist');
}
}
You can set in you modal:
public function FirstImage()
{
return $this->hasOne('image');
}
then:
$data = Artist::with('FirstImage')->get();

Laravel 4: Select row if a relation exists by querying relation

I am trying to query a products table, and want it to return a collection if a relation exists.
Iteration 1 below queries all rows in the products table, and lazy loads the metals table if $name matches. This is wrong.
My Route:
Route::group(array('prefix' => '{api}/v1'), function()
{
Route::controller('products', 'Api\V1\ProductController');
});
My Controller:
public function getFilter($metal = null) {
$products = $this->product;
if ($metal) {
$products->with('metal', function($query, $metal) {
$query->where('name', $metal);
});
}
return Response::api($products->get());
}
I want only $products to display if metal.name = $metal. e.g. something like:
$this->products->where('metal.name', $metal)->get;
Solution using part of Glad To Help's answer:
This provides an alternative approach 2, without the need for joins.
http://paste.laravel.com/WC4
Unfortunately you cannot do this with one swipe in Eloquent yet.
BUT, there is a way by using the inverse relation, like this:
public function getFilter($metal = null)
{
// filter the metals first
$metals = Metal::with('products')->where('name', '=' , $metal)->get();
$products = array();
foreach($metals as $metal)
{
// collect the products from the filtered metals
$products = array_merge($products, $metal->products->toArray() );
}
return $products;
}
If this is not elegant solution for you, you will either have to use Fluent to construct the query and join the products x metals table manually or pre-join them by overriding the newQuery() method.
1) alternative approach one.
public function getFilter($metal = null) {
return DB::table('products')->join('metal', 'products.id', '=' , 'metal.product_id')
->where('metal.name', $name)
->select(array('products.*'));
}
2) alternative approach two
class Product extends Eloquent{
public function newQuery($excludeDeleted = true){
return parent::newQuery()->join('metal','id','=','metal.product_id');
}
}

Return View() or PartialView()? How to decide?

I hava an Action:
public ActionResult GetOrders(int id)
{
...
}
When I access it through hyperlink(~/Order/GetOrders/1), I want GetOrder return View(), the whole page.
When through #Html.Action("GetOrders"), I want it return PartialView() to be a part of a page.
Now i settled the problem using Erik Philips's method.
public ActionResult GetOrders(int id)
{
var orders = db.Order.Where(a => a.AdCompanyID == id).ToList();
ViewBag.AdCompanyName = db.AdCompany.Where(a => a.ID == id).Select(a => a.Name).First().ToString();
if (ControllerContext.IsChildAction)
{
ViewBag.isPartial = true;
return PartialView(orders);
}
ViewBag.isPartial = false;
return View(orders);
}
#{Html.RenderAction("GetOrders", new { id = Model.ID });}
#Html.ActionLink("Related orders", "GetOrders", new { id = item.ID })
in GetOrders.cshtml:
#if (ViewBag.isPartial == false)
{
...
}
to generate different view.
Queti M. Porta thanks all the same!
You can use the ControllerContext.IsChildAction.
public ActionResult Foo()
{
if (ControllerContext.IsChildAction)
{
return PartialView("GetOrdersPartial", model);
}
return View("GetOrders", model);
}
Also, I would recommend using Html.RenderAction.
Updated per Comment
I'd also mention that I've never had the need to do this, in my own experience. Either you really have a completely different view, or you are unaware that PartialView will return a view without a Layout.
An easy way would be to pass in a parameter into the action method to let it know how you want the view rendered.
public ActionResult GetOrders(int id, bool? isPartial)
{
return (isPartial.HasValue() && isPartial.Value)
? PartialView()
: View();
}
In the above example, we are passing in the isPartial, however, you can also check to see if the request was done via ajax using Request.IsAjaxRequest
Other than that, there aren't many other ways to determine the method of the request.