OctoberCMS - Model query with relation manyToMany - laravel

Once again i'm asking some help from the community...
Scenario:
I have two tables with one pivot table (something like posts and tags to give some context):
table 1 (Evento):
-id
-....
table 2 (Etiquetas):
-id
-etiqueta
pivot table (Eventos_Etiquetas):
-evento_id (pk)
-etiqueta_id (pk)
The relations are set as:
public $belongsToMany = [
'eventos' => ['JML\Gkb\Models\Evento', 'table' => 'jml_gkb_eventos_etiquetas']
];
and
public $belongsToMany = [
'etiquetas' => ['JML\Gkb\Models\Etiqueta', 'table' => 'jml_gkb_eventos_etiquetas']
];
Now, what i want to achieve:
-get all events that have any individual tag without regarding the order of input (or).
-get all events that have all tags without regarding the order of input (and).
As you can imagine i'm strugling with this as i'm new to October/Laravel query builder (and not so to sql).
What i've done so far:
if (Session::get('tipo') == 'etiqueta'){
$pesquisa = preg_split('/\s+/', $temp, -1, PREG_SPLIT_NO_EMPTY);
if (Session::get('modo') == 0){
$this['records'] = Evento::with('etiquetas')->whereHas('etiquetas', function($query) use ($pesquisa){
foreach ($pesquisa as $palavra){
$query->where('etiqueta', 'like', "%$palavra%");
}
})->orderBy('id', 'DESC')->paginate(25);
}
if (Session::get('modo') == 1){
$this['records'] = Evento::with('etiquetas')->whereHas('etiquetas', function($query) use ($pesquisa){
foreach ($pesquisa as $palavra){
$query->orWhere('etiqueta', 'like', "%$palavra%");
}
})->orderBy('id', 'DESC')->paginate(25);
}
}
The user input is passed by $temp variable and it's splited to words to the $pesquisa array variable. 'modo' defines if the user pretend a search by AND (0) or by OR (1). Based on that choice a query is built to try to get the results using $palavra variable as any word of $pesquisa.
The result of this:
In modo == 0, i only can get the events of one tag of user input, if it have more than one word (any letter that don't exist on first word) don't get any result.
In modo == 1 it gets all events.
In both cases i don't get any event that don't have any tag (etiqueta) - correct behaviour.
I've tried some other ways but with no avail... This one looks to me the most logical of the tries... Can someone point me on the correct direction ?
TIA
JL

After some tries i have half of the problem solved. The part where i want to get any "Evento" that has any of the "Etiqueta" on user input not regarding the order of them is working fine finally.
Resume bellow
if (Session::get('tipo') == 'etiqueta'){
$pesquisa = preg_split('/\s+/', $temp, -1, PREG_SPLIT_NO_EMPTY);
$cadeiapesquisa = implode('|', $pesquisa);
/***** NOT WORKING YET *****/
if (Session::get('modo') == 0){
$this['records'] = Evento::with('etiquetas')->whereHas('etiquetas', function($query) use ($pesquisa){
foreach ($pesquisa as $palavra){
$query->where('etiqueta', 'like', "%$palavra%");
}
})->orderBy('id', 'DESC')->paginate(25);
}
/****** THIS IS WORKING FINE ! *******/
if (Session::get('modo') == 1){
if ( count ($pesquisa) > 0 && !($temp == null)){
$this['records'] = Evento::with('etiquetas')->whereHas('etiquetas', function($query) use ($cadeiapesquisa){
$query->where('etiqueta', 'regexp', "$cadeiapesquisa");
})->orderBy('id', 'DESC')->paginate(25);
} else {
Evento::paginate(25);
}
}
}
The first parte is fighting, but i will get there. :)
TIA
JL
[EDIT]
Problem solved... The resulting snipet of code working so far with the tests i've done is bellow:
if (Session::get('tipo') == 'etiqueta'){
$pesquisa = preg_split('/\s+/', $temp, -1, PREG_SPLIT_NO_EMPTY);
$cadeiapesquisa = implode('|', $pesquisa);
$contagem = count($pesquisa);
if (Session::get('modo') == 0){
if ( strlen($cadeiapesquisa) > 0 ){
$this['records'] = Evento::with('etiquetas')->whereHas('etiquetas', function($query) use ($cadeiapesquisa, $contagem){
$query->where('etiqueta', 'regexp', "$cadeiapesquisa")->groupBy('evento_id')->having(DB::raw("COUNT('etiqueta_id')"), '>=', $contagem );
})->paginate(25);
} else {
$this['records'] = Evento::paginate(25);
}
}
if (Session::get('modo') == 1){
if ( strlen($cadeiapesquisa) > 0 ){
$this['records'] = Evento::with('etiquetas')->whereHas('etiquetas', function($query) use ($cadeiapesquisa){
$query->where('etiqueta', 'regexp', "$cadeiapesquisa");
})->paginate(25);
} else {
$this['records'] = Evento::paginate(25);
}
}
}
JL

Related

Laravel many to many relationship making a list with pivot table for order

I have made a list of all items using many to many relationship.
With a pivot I make sure that there is an order.
Now I am trying to remove 1 item from the list.
And the order numbers must also move, this does not work.
From the view I provide the $id of the item and the order number.
public function removefromlist($id, $nummer)
{
$user = User::find(auth()->user()->id);
$user->manytomany()->detach($id);
$nummerlist = $user->manytomany()->count();
for($i = $nummer + 1;$i <= $nummerlist;$i++){
$testt = $user->manytomany()->where('nummer', $i);
$user->manytomany()->updateExistingPivot($testt->first()->item_id , ['nummer' => $i -1]);
}
return view('welcome')->with('itemslist', $user->manytomany);
}
How can I ensure that when I delete an item, the other move up?
You can try the following
public function removefromlist($id, $nummer)
{
$user = auth()->user();
$user->manytomany()->detach($id);
$user->manytoMany()
->where('nummer', '>', $nummer)
->get()
->each(function($item) use($user) {
$user->manytomany()
->updateExistingPivot(
$item->id,
['nummer' => $item->pivot->nummer - 1]
);
});
return view('welcome')->with('itemslist', $user->manytomany);
}

How to put the condition on User table with condition on subject table

Need to find the record with either first_name of the User table equal the keyword or subject of the TeacherSubject matches the keyword but 'step_completed' should equal '5' and 'is_approved' should equal '1' from the User table even if the orwhereHas condition is true.
$condition_array['radius'] = 25;
$condition_array['lat'] = $request->input('lat');
$condition_array['lng'] = $request->input('lng');
$condition_array['keyword'] = $request->input('keyword');
$sorting_type = $request->input('sorting_type');
$condition_array['filter'] = $request->input('filter');
if($sorting_type != ""){
if($sorting_type == 0){
$order_column = 'first_name';
$order_order = 'DESC';
}
if($sorting_type == 1){
$order_column = 'first_name';
$order_order = 'ASC';
}
}else{
$order_column = 'id';
$order_order = 'ASC';
}
$teachers = User::select('id','email','first_name','last_name','profile_picture','country_code','mobile')->with('teacherSubject')
->where(function($query) use($condition_array){
$query->where(['step_completed'=>5,'is_approved'=>1]);
$query->where(['first_name'=>$condition_array['keyword']]);
$query->orWhereHas('teacherSubject', function ($query1) use($condition_array){
$query1->where('subject',$condition_array['keyword']);
});
})
->orderBy($order_column,$order_order)
->get();
Need to find the record with either first_name of the User table equal the keyword or subject of the TeacherSubject matches the keyword but 'step_completed' should equal '5' and 'is_approved' should equal '1' from the User table even if the orwhereHas condition is true.
So if I would translate your statement into a query, I think it should look like this:
$teachers = User
::select('id','email','first_name','last_name','profile_picture','country_code','mobile')
->with('teacherSubject')
->where(['first_name'=>$condition_array['keyword']])
->orWhere(function($query) use($condition_array) {
$query->whereHas('teacherSubject', function($teacherQuery) use($condition_array) {
$teacherQuery->where('subject',$condition_array['keyword']);
})
->where(['step_completed'=>5,'is_approved'=>1]);
})
->orderBy($order_column,$order_order)
->get();
This way you have 2 conditions:
users.first_name == keyword OR
teacher_subject_user_id == users.id AND teacher_subject.subject = keyword AND users.step_completed == 5 AND is_approved == 1
If any of both is true, it will show up as a result.
Please let me know if it works :)

How to get 3 conditions in laravel

guys so I want to get 3 conditions in controller Laravel, So I build a post with the comment system. My comment has 3 conditions, from default condition it will get value = 0, when is approved it will get value = 1, when it's denied it will give value = 2. I want to get 3 conditions to count how many it is because I want to build another value like value = 3 or 4 or 5 for another condition so I won't use get all.
Here is my comment controller function code
private function getCountComment()
{
$user = Auth::user();
$comcount = $user->competitions;
foreach ($comcount as $key => $value) {
$count = Comment::where('id_post', $value->id)
->where('is_accepted', '=', 0 AND 1 AND 3)
->count();
$comcount[$key]->comment_to_count = $count;
}
return $comcount;
}
I try that code but only get the first condition is_accepted = 0.
Hope you guys can help me.
Try this code
private function getCountComment(){
$user = Auth::user();
$comcount = $user->competitions;
foreach ($comcount as $key => $value) {
$comcount[$key]->comment_to_count = Comment::where('id_post', $value->id)->whereIn('is_accepted', [0,1,3])->count();
}
return $comcount;
}
or you can
Comment::where('id_post', $value->id)->where(function($query) {
$query->where('is_accepted', '=', 0)
->orWhere('is_accepted', '=', 1)
->orWhere('is_accepted', '=', 3)
})->count();

Alias name in having clause in laravel 5.1

I'm using a function which is using alias name in "HAVING" clause and I'm getting an error,
"Unknown column 'orderStatusWithoutOpen' in 'having clause'".
Here is my code:
public static function getEquipmentPaginated($conditions = false, $id = false,$orderby='equipment_no',$order='asc',$filter=false)
{
$equipment = Equipment::select('checklist.*');
$equipment->leftjoin('checklist','equipment_id','=','equipment.id');
$equipment->addSelect('equipment.*', DB::Raw("(SELECT count(ordre.status = 001) FROM ordre LEFT JOIN checklist ON checklist.ordre_id=ordre.id WHERE checklist.equipment_id=equipment.id and ordre.status = 001 order by ordre.start_date desc limit 1) AS orderStatusWithoutOpen"));
$equipment->groupBy('equipment.id');
$start = date('Y-m-d');
$end = date('Y-m-d', strtotime('+15days'));
if($filter == '1' && $filter != "") {
$equipment->having('orderStatusWithoutOpen', "<", $filter);
}
if($filter == $start) {
$equipment->whereBetween('certificate.valid_date', [$start, $end]);
}
if($id) {
$equipment->where('equipment.customer_id', '=', $id);
}
return $equipment->paginate(10);
}
Can anyone please tell me what mistake I'm making here.
Thanks in advance.
Actually I corrected this myself. The query is working good and problem is with the pagination. So what I did is, I return the result within the if condition where I'm using 'HAVING' clause. Like Mentioned below:
if($filter == '1' && $filter != "")
{
$equipment->having('orderStatusWithoutOpen', "<", $filter);
return $equipment->get();
}
You have to bring the value in select query on which you want to order the result.
For example if equipment_fabricat.fabricat_name THEN
get that value in select(equipment_fabricat.fabricat_name) before applying order by

Laravel how do I get the row number of an object using Eloquent?

I'd like to know the position of a user based on its creation date. How do I do that using Eloquent?
I'd like to be able to do something like this:
User::getRowNumber($user_obj);
I suppose you want MySQL solution, so you can do this:
DB::statement(DB::raw('set #row:=0'));
User::selectRaw('*, #row:=#row+1 as row')->get();
// returns all users with ordinal 'row'
So you could implement something like this:
public function scopeWithRowNumber($query, $column = 'created_at', $order = 'asc')
{
DB::statement(DB::raw('set #row=0'));
$sub = static::selectRaw('*, #row:=#row+1 as row')
->orderBy($column, $order)->toSql();
$query->remember(1)->from(DB::raw("({$sub}) as sub"));
}
public function getRowNumber($column = 'created_at', $order = 'asc')
{
$order = ($order == 'asc') ? 'asc' : 'desc';
$key = "userRow.{$this->id}.{$column}.{$order}";
if (Cache::get($key)) return Cache::get($key);
$row = $this->withRowNumber($column, $order)
->where($column, '<=',$this->$column)
->whereId($this->id)->pluck('row');
Cache::put($key, $row);
return $row;
}
This needs to select all the rows from the table till the one you are looking for is found, then selects only that particular row number.
It will let you do this:
$user = User::find(15);
$user->getRowNumber(); // as default ordered by created_at ascending
$user->getRowNumber('username'); // check order for another column
$user->getRowNumber('updated_at', 'desc'); // different combination of column and order
// and utilizing the scope:
User::withRowNumber()->take(20)->get(); // returns collection with additional property 'row' for each user
As this scope requires raw statement setting #row to 0 everytime, we use caching for 1 minute to avoid unnecessary queries.
$query = \DB::table(\DB::raw('Products, (SELECT #row := 0) r'));
$query = $query->select(
\DB::raw('#row := #row + 1 AS SrNo'),
'ProductID',
'ProductName',
'Description',
\DB::raw('IFNULL(ProductImage,"") AS ProductImage')
);
// where clauses
if(...){
$query = $query->where('ProductID', ...));
}
// orderby clauses
// ...
// $query = $query->orderBy('..','DESC');
// count clause
$TotalRecordCount = $query->count();
$results = $query
->take(...)
->skip(...)
->get();
I believe you could use Raw Expresssions to achieve this:
$users = DB::table('users')
->select(DB::raw('ROW_NUMBER() OVER(ORDER BY ID DESC) AS Row, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
However, looking trough the source code looks like you could achieve the same when using SQLServer and offset. The sources indicates that if you something like the following:
$users = DB::table('users')->skip(10)->take(5)->get();
The generated SQL query will include the row_number over statement.
[For Postgres]
In your model
public function scopeWithRowNumber($query, $column = 'id', $order = 'asc'){
$sub = static::selectRaw('*, row_number() OVER () as row_number')
->orderBy($column, $order)
->toSql();
$query->from(DB::raw("({$sub}) as sub"));
}
In your controller
$user = User::withRowNumber()->get();

Resources