Laravel relation with custom join condition - laravel

I received quite strange database to work with and I'm trying to create relation on model Invoice like below:
public function order()
{
return $this->hasOneThrough(Order::class, Shipment::class,
'id',
'id',
DB::raw('-order_id'),
Shipment::COL_ORDER_ID
);
}
but it gives me the error array_key_exists(): Argument #1 ($key) must be a valid array offset type.
Of course the problem is in the DB::raw part... As you may have guessed I'm trying to create join condition like -Invoice.order_id = Shipment.id or ABS(Invoice.order_id) = Shpment.id will be fine too because only order_id on the Invoice needs to be converted into positive number.
Any ideas to get it to work with minimal additional code as possible? Any help will be appreciated :)

Related

How to write a natural join on eloquent?

I'm trying to write a natural joint on eloquent but without success, please could you help me?
So my sql request is:
SELECT * FROM `case_managers` NATURAL JOIN risks
I have did
Case_managers::join('risks')->get()
Thank you :)
You can use 'with' instead of join like
Case_managers::with('risk')->get();
where risk is your model name related to risks table.
Create a relationship in Case_managers model.
public function risks()
{
return $this->hasOne('App\Models\Risk','foreign_key','local_key');
//if there is going to be multiple entries,change `hasOne` as `hasMany`
}
You can access risks as
$res = Case_managers::find($id);
echo $res->risks->risk_field; //risk_field is the column name you want from risks table
//if `hasMany` is the relationship,use `foreach`
foreach($res->risks as $risk)
{
echo $risk->risk_field;
}

Laravel adding custom attribute to where clause

I have a mutator that calculate the product existing quantity by:
summing the product credit and minus it from the sold credit.
public function getReportTotalQuantityAttribute() {
// GET IMPORT INVOICE SUM
$import_invoices_sum = $this -> credits -> sum('quantity');
// GET EXPORT INVOICE SUM
$export_invoices_sum = $this -> sold -> sum('quantity');
// CALCULATE THE SUM
return $import_invoices_sum - $export_invoices_sum;
}
This mutator works fine and return the actually product quantity as report_total_quantity attribute whenever I call the model.
What I am trying to do:
I am trying to get the product where('report_total_quantity', '>', 0).
What I have tried:
$condition_queries = Product::withCreditsAndWarehouses() -> where('report_total_quantity', '>', 0);
But I am getting error say Unknown column 'report_total_quantity'. And this is logical since I don't have this column but I append it using $append and mutator.
What I searched and found:
I have found something called filters but I don't think it is good solution since I am using paginate(10) and like that I will return 10 values and filter them.
Also I have many other conditions and filter is not working good for me.
If you want to use WHERE with attribute create by mutator You must first create Collection then use Where statement.
$condition_queries = Product::withCreditsAndWarehouses()->get()->where('report_total_quantity', '>', 0);
But after ->get() you can not use paginate. Instead you must use Collection paginate method forPage. Check this

Laravel update query use

I used this query to update the status column.
$val="1";
vehicles::where('id' , '=' , $veh_status)->update(['status' => $val]);
But when I submitted the status value doesn't change.
you can trace your query by using ->toSql() method !
try this to find whats happening in back
Not sure what the problem is there because you haven't given much info to work with, but you can check these suggestions:
Check if the column is set to be mass assignable in the model class, that is, it is in the fillable[] array.
make sure the id you pass to the where() function is valid.
Try using another function, save() which will achieve the same results you seek, like this;
// filter the vehicle
$vehicle = vehicles::where('id', '=', $veh_id)->first();
or
$vehicle = vehicles::find($veh_id);
$vehicle->status = 1;
$vehicle->save();
Lastly, I noticed your id variable you pass to the where the () function is called $veh_status "presumably - vehicle status" and not $veh_id, "presumably - vehicle id" so probably check that out.
Ref: Laravel Model Update documentation

Using Laravel 5, how do I get all() and order by belongsToMany relationship by the pivot table

I have Deals and Faq's. I have functional relationships working and I can reference $deal->faqs() and it returns the right faqs.
The problem I am trying to solve comes up as I administer the faqs related to a deal. In my Deal admin view (new / edit) I am getting all the Faq's.
$faqs = \App\Faq::all();
This works great, and I am even able to check if an faq is related to a deal through my checkbox: in the view:
{!! Form::checkbox('faqlist[]', $faq->id, $deal->faqs->contains($faq->id) ? true : false) !!}
So now we have a list of all the faqs and the correct ones are checked.
I have setup an order column on the pivot table (deal_faq). That table consists of:
deal_id
faq_id
timestamps
order
In my form, I have a drag and drop ordering solution (js) built and working. By working I mean, I can drag/drop and a hidden field value is updated to reflect the correct order.
When creating a deal, this is no problem. Get all the faq's, check a few to associate, drag to order, then save.
When editing a deal, I need to load based on the order column in the deal_faq table. This is my issue.
I have tried a few things but always get an error. An example of what I have tried is:
$faqs = \App\Faq::orderBy('deal_faq.order', 'asc')->get();
This returns an error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'deal_faq.order' in 'order clause' (SQL: select * from `faq` order by `deal_faq`.`order` asc)
I think the issue is that I am trying to get all, but order by a field that only exists for the related faqs since the order field is on the deal_faq. Just not sure how to solve.
In essence you need to join the pivot table and then apply the order
$faqs = \App\Faq::join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc')
->get();
You may need to adjust table and column names to match your schema.
Now you can extract this logic into a scope of the Faq model
class Faq extends Model
{
...
public function scopeOrdered($query)
{
return $query->join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc');
}
}
and then use it like this
$faqs = \App\Faq::ordered()->get();
UPDATE:
This works to get the FAQ's in order but it only get the ones that
have been associated. There will be FAQ's that are not associated and
thus not ordered.
In this case you just need to use an outer join - LEFT JOIN. The scope definition would then look like this
public function scopeOrdered($query)
{
return $query->join('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id', 'left')
->orderBy('deal_faq.order', 'asc');
}
or a bit more expressively
public function scopeOrdered($query)
{
return $query->leftJoin('deal_faq', 'faqs.id', '=', 'deal_faq.faq_id')
->orderBy('deal_faq.order', 'asc');
}
Please consider adding a secondary order (i.e. by id, or any other field(s) in faqs table that make sense). This way you'd have a deterministically ordered resultsets each time regardless of whether you have an explicit order defined in a pivot table or not.

Laravel validation: unique with multiple columns and soft_delete

I am trying to do a Laravel validation rules as follow:
"permalink" => "required|unique:posts,permalink,hotel_id,deleted_at,NULL|alpha_dash|max:255",
The explanation to the rules is:
I have a table "Posts" in my system with the following fields (among others): hotel_id, permalink, deleted_at. If MySQL would allow make an unique index with null values, the sql would be:
ALTER TABLE `posts`
ADD UNIQUE `unique_index`(`hotel_id`, `permalink`, `deleted_at`);
So: I just add a new row IF: the combination of hotel_id, permalink and deleted_atfield (witch must be NULL) are unique.
If there is already a row where the permalink and hotel_id field are the same and 'deleted_at' field is NULL, the validation would return FALSE and the row wouldnt be inserted in the database.
Well. I don't know why, but the query Laravel is building looks like:
SELECT count(*) AS AGGREGATE FROM `posts`
WHERE `hotel_id` = the-permalink-value AND `NULL` <> deleted_at)
What the heck...
The query I was hoping Laravel build to validation is:
SELECT count(*) AS AGGREGATE FROM `posts`
WHERE `permalink` = 'the-permalink-value' AND `hotel_id` = ? AND `deleted_at` IS NULL
Could someone explain me how this effectively works? Because everywhere I look it looks like this:
$rules = array(
'field_to_validate' =>
'unique:table_name,field,anotherField,aFieldDifferentThanNull,NULL',
);
Does anyone could help me?
Thank you
all.
Finally, I got a proper understanding of the validation (at least, I think so), and I have a solution that, if it is not beautiful, it can helps someone.
My problem, as I said before, was validate if a certain column (permalink) is unique ONLY IF other columns values had some specific values. The problem is the way Laravel validation string rules works. Lets get to it:
First I wrote this:
"permalink" => "required|unique:posts,permalink,hotel_id,deleted_at,NULL|alpha_dash|max:255",
And it was generating bad queries. Now look at this:
'column_to_validate' => 'unique:table_name,column_to_validate,id_to_ignore,other_column,value,other_column_2,value_2,other_column_N,value_N',
So. The unique string has 3 parameters at first:
1) The table name of the validation
2) The name of the column to validate the unique value
3) The ID of the column you want to avoid (in case you are editing a row, not creating a new one).
After this point, all you have to do is put the other columns in sequence like "key,value" to use in your unique rule.
Oh, easy, an? Not so quickly, paw. If you're using a STATIC array, how the heck you will get your "currently" ID to avoid? Because $rules array in Laravel Model is a static array. So, I had to came up with this:
public static function getPermalinkValidationStr() {
$all = Input::all();
# If you are just building the frozenNode page, just a simple validation string to the permalink field:
if(!array_key_exists('hotel', $all)) {
return 'required|alpha_dash|max:255';
}
/* Now the game got real: are you saving a new record or editing a field?
If it is new, use 'NULL', otherwise, use the current id to edit a row.
*/
$hasId = isset($all['id']) ? $all['id'] : 'NULL';
# Also, check if the new record with the same permalink belongs to the same hotel and the 'deleted_at' field is NULL:
$result = 'required|alpha_dash|max:255|unique:posts,permalink,' . $hasId . ',id,hotel_id,' . $all['hotel'] . ',deleted_at,NULL';
return $result;
}
And, in the FrozenNode rules configuration:
'rules' => array(
'hotel_id' => 'required',
'permalink' => Post::getPermalinkValidationStr()
),
Well. I dont know if there is a easiest way of doing this (or a much better approach). If you know something wrong on this solution, please, make a comment, I will be glad to hear a better solution. I already tried Ardent and Observer but I had some problems with FrozenNode Administrator.
Thank you.

Resources