overriding replationship column to show only users with specific role - laravel

I created BREAD for using voyager with relationship column to display all the users and i want this column to show only users with admin role.
I tried to override the view but I think its wrong choice any help?
#if($row->field == 'lead_belongsto_user_relationship')
#endif
I can reach the column in the view/add view but I don't know how to edit the array of results or how to override the query.
It is showing all the users I want to show only admin list.

I finally did it Using custom Controllers.
Steps:
First create new controller
php artisan make:controller LeadController
Extend Voyagers controller
<?php
namespace App\Http\Controllers;
class LeadController extends \TCG\Voyager\Http\Controllers\VoyagerBaseController
{
//...
}
?>
Finally you can override the relation function from VoyagerBaseController
if($request->type == "lead_belongsto_user_relationship"){
if ($search) {
$total_count = app($options->model)->where($options->label, 'LIKE', '%'.$search.'%')->count();
$relationshipOptions = app($options->model)->take($on_page)->skip($skip)
->where($options->label, 'LIKE', '%'.$search.'%')->where("role_id",3)
->get();
} else {
$total_count = app($options->model)->count();
$relationshipOptions = app($options->model)->take($on_page)->skip($skip)->where("role_id",3)->get();
}
}
Here i overrided it to select only users with role_id = 3 you can customize it as you need.
After that go to the BREAD-settings and fill in the Controller Name
with your fully-qualified class-name in my case it is
"\App\Http\Controllers\LeadController"

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

Laravel Backpack 4.0. Save a model then another one (different)

In my laravel/backpack 4.0 project I have three model:
Product: id, name, ...
Pricebook: id, name, ...
Price: id, pricebook_id, value, ...
I need to create a row in prices table every time a new Product is stored id database.
Is there a method like 'before' or 'after' save to hook?
if not, any other solution?
...I got confused about something trivial, probably a mispelling. This is the solution thank's Martin
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests\ProductRequest;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
use App\Models\Price;
class ProductCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation {store as traitStore ;}
use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;
public function store()
{
$request = $this->crud->validateRequest();
$response = $this->traitStore();
//get last inserted row id
$entryID = $this->data['entry']->id;
$price = new Price();
$price->product_id = $entryID;
$price->price = 0;
$price->save();
// show a success message
\Alert::success('New price created in DEFAULT_BASE_PRICEBOOK'))->flash();
return $response;
}
...
There are no such hooks, but you can override these methods that control the database insert/update. From the documentation:
Callbacks
Developers coming from GroceryCRUD on CodeIgniter or other CRUD systems will be looking for callbacks to run before_insert, before_update, after_insert, after_update. There are no callbacks in Backpack. The store() and update() code is inside a trait, so you can easily overwrite that method, and call it inside your new method. For example, here's how we can do things before/after an item is saved in the Create operation:
namespace App\Http\Controllers\Admin;
use Backpack\CRUD\app\Http\Controllers\CrudController;
class ProductCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation { store as traitStore; }
// ...
public function store()
{
// do something before validation, before save, before everything
$response = $this->traitStore();
// do something after save
return $response;
}
Source

Filter many to many relationship based on child existence and column value

I've been searching for a while and couldn't find an answer, here's what I have:
1- ShowCategory (id & title)
class ShowCategory extends Model
{
public function shows()
{
return $this->belongsToMany(Show::class, 'category_show');
}
}
2- Show (id, title & active)
class Show extends Model
{
public function categories()
{
return $this->belongsToMany(ShowCategory::class, 'category_show');
}
}
So there's a many to many relationship, what I need is retrieving all ShowCategory elements that has at least one Show related to it, and to filter each ShowCategory->shows by show.active, only return shows that are active
Here's what I'm trying to do:
$categories = ShowCategory::whereHas('shows', function($query) {
$query->where('shows.active', '=', true);
})->get();
It only filters ShowCategory that includes shows and if only one of those shows are active, it returns the category with all shows inside, even if others are not active, I need to filter those who are not active.
What should I do? Thanks in advance
This requires a combination of whereHas() and with(). First, whereHas() will filter the ShowCategory model to those that have an active Show, while the with() clause will limit the results of the relationship to only return active ones:
$categories = ShowCategory::whereHas("shows", function($subQuery) {
$subQuery->where("shows.active", "=", true); // See note
})->with(["shows" => function($subQuery){
$subQuery->where("shows.active", "=", true);
}])->get();'
Note: You should be able to use active instead of shows.active, but depends on if that column is on multiple tables.
Using this query, you will get a Collection of ShowCategory models, each with their active Show models already loaded and available via ->shows:
foreach($categories AS $category){
dd($category->shows); // List of `active` Shows
}
This is what you need.
$categories = ShowCategory::whereHas('shows', function($query) {
$query->whereActive(true);
})->get();
Try, this can be a possible way to retreive related results.
// This will only return ShowCategory which will have active shows.
/* 1: */ \ShowCategory::has('shows.active')->get();
// So, logically this will only have active shows -__-
$showCategory->shows
Laravel allows to extends foreign relation by using this . notation as a condition for retreival.
Update
You should update the \ShowCategory model as
public function shows(){
return $this->belongsToMany(Show::class, 'category_show')->where('active', true);
}

Conditional BREAD in Laravel / Voyager

Is there a way to apply certain conditions to the list of records displayed by Voyager's BREAD? For example, only display records where a certain column is blank (i.e. WHERE 'col_name' IS NULL)?
UPDATE (17/12/18):
You can override the controller and add the where clause. For example, if you need to override "Posts" list, then you should:
Make a custom PostController: php artisan make:controller PostController
Extend it from Voyager BREAD controller: class PostController extends \TCG\Voyager\Http\Controllers\VoyagerBaseController
Import Voyager's Facade: use TCG\Voyager\Facades\Voyager;
Copy the whole index function from VoyagerBaseController. You can find it in \vendor\tcg\Voyager\Http\Controllers\VoyagerBaseController
Override it to add any logic or filter you need, for example:
$dataType = Voyager::model('DataType')
->where('slug', '=', $slug)
->where('col_name', '=', NULL)
->first();
You can also set $orderBy = 'col_name' and $sortOrder = 'asc'/'desc' custom values in the same function. Here Voyager's docs.
OLD:
Yes, there is. You need to edit the view and apply a condition filter. Here is explained how to override views (and controllers if you want to filter it before sending the data to the view).
Just define a Scope in your model:
public function scopeMyScope($query)
{
return $query->where('col_name', null);
}
then go to Bread Form and select it form Scope drop down :)

Retrieve all columns from table A and some from table B using laravel eloquent

I am trying to retrieve the thumb image path by joining the images table to the listing table. As such, I have the following query in my controller.
$listings = Listing::select('listings.*, images.path as image_path')
->where('listings.ownerid', '=', $ownerid)
->leftJoin('images', 'listings.thumbId', '=', 'images.id')->get();
After testing out the function, the query fails since laravel interprets the query as
select `listings`.`*, images`.`path` as `image_path` from `listings` left join `images` on `listings`.`thumbId` = `images`.`id` where `listings`.`ownerid` = 1)
Notice the asterisk (*) is joined with the ", images" word making it '*, images'.
The query works fine without laravel's odd typo. How does one fix this issue?
You need to do one change in your query. You are passing raw select fields so you need to use selectRaw() instead of select(). Like
$listings = Listing::selectRaw('listings.*, images.path as image_path')
->where('listings.ownerid', '=', $ownerid)
->leftJoin('images', 'listings.thumbId', '=', 'images.id')->get();
check by try above query.
I suggest you to use Laravel Eloquent Relationships feature. Since your code above is more like Query Builder rather than Eloquent. Let's see the example bellow:
You will have 2 Models, 1 for each table (listings, images):
App\Listing Model:
<?php
...
use App\Image;
class Listing extends Eloquent {
...
protected $table = 'listings';
// define Eloquent Relationship of Listing model to Image model
public function image() {
return $this->belongsTo(Image::class, 'thumbId');
}
...
}
App\Image Model:
<?php
...
use App\Listing;
class Image extends Eloquent {
...
protected $table = 'images';
...
// define Eloquent Relationship of Image model to Listing model
public function listings() {
return $this->hasMany(Listing::class, 'thumbId');
}
}
So how to get the data?
// get all listing data
$listings = Listing::all();
// loop through the data
foreach ($listing as $listing) {
dump($listing->id);
// because we have define the relationship, we can access the related data of image
dump($listing->image->path);
// call $this->image will return related Image model
dump($listing->image);
}
You can see Laravel official documentation for more example and explanation.
Hope it helps.

Resources