I was wondering if its possible to group appointments by dates with 1 query so my response will be something like this (in JSON):
{
"appointmentsByDay":[
"01-07-2018":[
{
"appointment":"test"
},
{
"appointment":"test"
}
],
"02-07-2018":[
{
"appointment":"test"
},
{
"appointment":"test"
}
],
..................
"31-07-2018":[
{
"appointment":"test"
},
{
"appointment":"test"
}
]
]
}
I know this is possible looping through all the days within a Month but then I have to call a similar query up to 31 times for a single month.
Currently I'm getting all my appointments in between a month the way below but these are not grouped by each day and appointments falling between a bigger range will get ordered wrong:
$startOfMonth = Carbon::parse($date)->startOfMonth();
$endOfMonth = Carbon::parse($date)->endOfMonth();
$appointments = Appointment::
where('user_id', '=', $userId)->
where(function ($query) use($endOfMonth, $startOfMonth) {
$query->where(function ($query) use($endOfMonth, $startOfMonth) {
$query->where('from_date', '<', $startOfMonth->format('Ymd'))
->where('to_date', '>', $endOfMonth->format('Ymd'));
})
->orWhere(function ($query) use($endOfMonth, $startOfMonth) {
$query->where('from_date', '>=', $startOfMonth->format('Ymd'))
->where('to_date', '<=', $endOfMonth->format('Ymd'));
})
->orWhere(function ($query) use($endOfMonth, $startOfMonth) {
$query->where('from_date', '<', $startOfMonth->format('Ymd'))
->where('to_date', '>=', $startOfMonth->format('Ymd'));
})
->orWhere(function ($query) use($endOfMonth, $startOfMonth) {
$query->where('from_date', '<=', $endOfMonth->format('Ymd'))
->where('to_date', '>', $endOfMonth->format('Ymd'));
});
})
->with(user')
->get();
My model looks like this:
namespace App\Models;
use Reliese\Database\Eloquent\Model as Eloquent;
class Appointment extends Eloquent
{
protected $table = 'appointments';
public $timestamps = false;
protected $fillable = [
'from_date',
'to_date',
'title'
];
}
My appointments table looks like this: (I'm using MYSQL)
+-----------+----------------+---------------------+-------------------+
| id (int) | title (varchar)| from_date (varchar) | to_date (varchar) |
+-----------+----------------+---------------------+-------------------+
| 1 | appointment1 | 20180725 | 20180725 |
+-----------+----------------+---------------------+-------------------+
| 2 | appointment2 | 20180726 | 20180726 |
+-----------+----------------+---------------------+-------------------+
| 3 | appointment3 | 20180723 | 20180812 |
+-----------+----------------+---------------------+-------------------+
| 4 | appointment4 | 20180726 | 20180726 |
+-----------+----------------+---------------------+-------------------+
| 5 | appointment5 | 20180612 | 20181123 |
+-----------+----------------+---------------------+-------------------+
Related
How can I update multiple rows at the same time by their report_id? My Inventory table looks like this:
| id | report_id | product_id | shelf_quantity | display_quantity |
|----|-----------|------------|----------------|------------------|
| 1 | 2 | 12 | 1 | 5 |
| 2 | 2 | 13 | 2 | 6 |
| 3 | 2 | 14 | 3 | 23 |
My attempt to solve the problem:
My controller:
function update($id, Request $request) {
$report = Report::find($id);
$inputs = $request->input('display');
$report->user_id = $user_id;
$report->save();
//Updating inventory table
$inventory = Inventory::where('report_id', $report->id)->get();
foreach($inputs as $key => $value) {
$inventory->display = $request->input('display')[$key] ?: 0;
$inventory->storage = $request->input('storage')[$key] ?: 0;
$inventory->save();
}
My web route:
Route::put('/reports/{id}', 'ReportController#update');
The problem here is that it is only saving the last value that I input.
Additional information
I'm also using the same code in storing new rows and it's working fine. Please see code below:
public function store(Request $request) {
//Adding inventory table
$new_inventories = new Inventory();
foreach($inputs as $key => $value) {
$data[] = [
'user_id' => $user_id,
'report_id' => $new_report->id,
'display' => $request->input('display')[$key] ?: 0,
'storage' => $request->input('storage')[$key] ?: 0,
'product_id' => $request->product_id[$key],
'created_at' => $now,
'updated_at' => $now,
// 'remark' => $request->remark[$key] ?? null,
];
}
Inventory::insert($data);
//End of inventory table
}
Any form of help would be appreciated. Thank you!
EDIT:
My form looks like this:
<form action="/reports/{{$report->id}}" method="post">
#csrf
{{method_field('PUT')}}
#foreach($products as $product)
<input type="number" name="product_id[]" value="{{ $product->id}}" hidden>
<input type="number" name="storage[]">
<input type="number" name="display[]">
#endforeach
</form>
You cannot update multiple rows in one shot. In this case the best way is to get one by one the items in inventory and update them. So you loop through $request->input('product_id'), get the Inventory model for that product_id and $report->id, update and save.
for($i=0; $i<count($request->input('product_id');$i++) {
$inventory = Inventory::where('report_id', $report->id)->where('product_id', $request->input('product_id')[$i])->first();
$inventory->display = $inventory->display = $request->input('display')[$i];
$inventory->storage = $inventory->storage = $request->input('storage')[$i];
$inventory->save();
}
I have 2 tables :
Product Option Group
id | opt_name | active_flag
------------------------------
1 | Cook level | 0
Product Option List
id | optgrp_id | list_name | active_flag
------------------------------------------
1 | 1 | 25 | 0
2 | 1 | 50 | 1
3 | 1 | 75 | 0
4 | 1 | 100 | 0
Product Option Group Model
public function getOptList()
{
return $this->hasMany(ProdOptlist::className(),['optgrp_id'=>'id']);
}
Product Option List Model
public function getOptGrp()
{
return $this->hasOne(ProdOptgrp::className(),['id'=>'optgrp_id']);
}
Product Option Group Controller
public function actionUpdate($id)
{
$model = $this->findModel($id);
if($model->load(Yii::$app->request->post()) && $model->validate())
{
...
}
else
return $this->render('update', ['model'=>$model]);
}
protected function findModel($id)
{
if (($model = ProdOptgrp::find()
->joinWith('optList')
->where([ProdOptgrp::tableName().'.id'=>$id,
ProdOptgrp::tableName().'.active_flag'=>0,
ProdOptlist::tableName().'.active_flag'=>0])
->one()) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
Update View
Expected output for print_r($model->optList) :
{
[id] => 1
[optgrp_id] => 1
[list_name] => 25
[active_flag] => 0
},
{
[id] => 3
[optgrp_id] => 1
[list_name] => 75
[active_flag] => 0
},
{
[id] => 4
[optgrp_id] => 1
[optList_name] => 100
[active_flag] => 0
}
Actual output :
{
[id] => 1
[optgrp_id] => 1
[list_name] => 25
[active_flag] => 0
},
{
[id] => 2
[optgrp_id] => 1
[list_name] => 50
[active_flag] => 1
},
{
[id] => 3
[optgrp_id] => 1
[list_name] => 75
[active_flag] => 0
},
{
[id] => 4
[optgrp_id] => 1
[optList_name] => 100
[active_flag] => 0
}
Yii2 debugger showing correct query but output still consist of all 4 elements.
Kindly advice if there is any mistake, thank you in advance :)
Even if you use the joinWith() the where() part of your $model = ProdOptgrp::find()... code only limits the result that is returned by the query executed by one() method call. It doesn't affect what is loaded for relations.
If you want to limit what is loaded for relations you can do it:
1) By modifing the existing relation
This is the solution you've come to. You add the where condition directly to the getOptList() method of your ProdOptgrp model. If you do it this way, the $model->optList will always return filtered relation. Depending on case that might be advantage or disadvantage.
2) By adding second relation
You can create another method in your ProdOptgrp model that will define the filtered relation while keeping the original getOptList() unfiltered.
For example like this:
class ProdOptgrp extends \yii\db\ActiveRecord
{
public function getOptList()
{
return $this->hasMany(ProdOptlist::className(),['optgrp_id'=>'id']);
}
public function getFilteredOptList()
{
return $this->getOptList()->where([
ProdOptlist::tableName() . '.active_flag' => 0
]);
}
}
In this case the $model->optList will still contain unfiltered OptLists and the $model->filteredOptList will contain filtered. This solution is good when you need to use both at different spots of your application.
3) By using callback syntax in joinWith() or with()
In case you want to filter the relation only in one particular case you don't need to modify your model. You can modify the relation with callback that is called before loading the data.
$model = ProdOptgrp::find()
->joinWith([
'optList' => function(\yii\db\ActiveQuery $query) {
$query->where([
ProdOptlist::tableName() . '.active_flag' => 0
]);
}
])->where([
ProdOptgrp::tableName().'.id'=>$id,
ProdOptgrp::tableName().'.active_flag'=>0,
ProdOptlist::tableName() . '.active_flag'=>0,
])->one();
The last condition in where() will cause the $model to be null when the active_flag is 0 in optgrp table but 1 in all related records in optlist table. I'm not sure if that is inteded behavior.
You can do it this way too:
//Controller
protected function findModel($id)
{
if (($model = ProdOptgrp::findOne($id)) !== null && $model->isActive()) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
//Model
/*
*Check if model is active
*/
public function isActive(){
return ($this->active_flag == 0) ? true: false;
}
public function getOptList()
{
return $this->hasMany(ProdOptlist::className(),['optgrp_id'=>'id'])->where([ProdOptgrp::tableName().'.active_flag'=>0]);
}
//View
foreach($model->optList as optItem): // you get filtered result
I'm using MySQL and i have schema like:
|------------|-------------|------------|--------------|
| cities |category_city| categories| companies |
|------------|-------------|------------|--------------|
| id | city_id | id | id |
| name | category_id | name |subcategory_id|
| | | parent_id | city_id |
| | | |...other cols |
|____________|_____________|____________|______________|
Relationships:
City with Category has ->belongsToMany()
public function categories()
{
return $this->belongsToMany(Category::class);
}
Categories has subcategories:
public function subcategories()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
And i'm getting companies from category and filtering by city, because i need the current city companies and for that i have a global scope:
public function getCompanies($city_id)
{
return $this->companies()->whereHas('mainCity', function ($q) use ($city_id) {
$q->where('city_id', $city_id);
});
}
mainCity method:
public function mainCity()
{
return $this->belongsTo(City::class, 'city_id');
}
Here is my method performing the query with AJAX request:
public function getPlaces(City $city, Category $subcategory, $north, $south, $east, $west)
{
$companies = $subcategory->companies()
->withCount('comments')
->companiesByBounds($north, $south, $east, $west)
->paginate(8);
$markers = $subcategory->companies()
->companiesByBounds($north, $south, $east, $west)
->get(['lat', 'lng', 'slug', 'title']);
return response()->json(['companies' => $companies, 'markers' => $markers], 200, [], JSON_NUMERIC_CHECK);
}
and by companiesByBounds scope method:
public function scopeCompaniesByBounds($query, $north, $south, $east, $west)
{
return $query->whereBetween('lat', [$south, $north])
->whereBetween('lng', [$west, $east]);
}
In companies i have ~2m records. The main problem is that the queries taking 3.5 seconds. Help please to improve my queries.
Here is the query:
select count(*) as aggregate from `companies` where `companies`.`category_id` = '40' and `companies`.`category_id` is not null and `lat` between '53.68540097020851' and '53.749703253622705' and `lng` between '91.34262820463869' and '91.51600619536134'
To improve speed you need to add indexes on the columns lat and lng.
CREATE INDEX idx_lat ON companies (lat);
The indexes are used in queries when the columns are added to conditions.
I'm trying to get data for each date in range from 3 tables: Main table which is connected with 2 other tables using hasMany method.
Data stored as in example:
Main Table:
id | article title | url | created_at | updated_at |
----------------------------------------------------------------
14 | Some Title | www.example.com | TIMESTAMP | TIMESTAMP |
Views table (there is written count of views for each hour):
id | article_id | views | created_at | updated_at |
---------------------------------------------------
1 | 14 | 317 | TIMESTAMP | TIMESTAMP | (01:00:00)
2 | 14 | 186 | TIMESTAMP | TIMESTAMP | (02:00:00)
Clicks Table (there is written every click on this article):
id | article_id | ip_adress | created_at | updated_at |
---------------------------------------------------------
1 | 14 | 192.168.1.1 | TIMESTAMP | TIMESTAMP |
For example:
I need to get Articles from 01-02-2016 to 01-03-2016.
For every article I need to sum views and clicks for each day.
So in result i need to get something like this:
ID: 14, Title: Some Title, Views: 503, Clicks: 27
First, I wrote this code, but it makes lots of requests to database:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
foreach ($dates as $i => $date) {
$articles = Articles::with(['views' => function ($query) use ($date) {
$query->where('created_at', $date);
}, 'clicks' => function ($query) use ($date) {
$query->where('created_at', $date);
}])->get();
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) {
// Code
}
foreach ($clicks->countOfClicks as $i) {
// Code
}
// Code
}
}
Then I found solution, to get exact same result as I'm getting in first example, but making only three requests:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
$articles = Articles::with(['views' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop));
}, 'clicks' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop));
}])->get();
foreach ($dates as $date) {
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
foreach ($clicks->countOfClicks as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
// Code
}
}
It solves problem with too many queries, but it takes to much time. Is it possible to do same thing faster?
Final Code:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
$articles = Articles::with(['views' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop))
->groupBy('campaign_id', DB::raw('DATE(created_at)'))
->selectRaw('*, sum(views) as views');
}, 'clicks' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop))
->groupBy('campaign_id', DB::raw('DATE(created_at)'))
->selectRaw('*, sum(clicks) as clicks');
}])->get();
foreach ($dates as $date) {
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
foreach ($clicks->countOfClicks as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
// Code
}
}
im making an api in laravel but when i send from a post request it display nothing it work only when i send the values in the url what im i doing wrong here is my code !
$user = new userInscription;
$user->nom = Request::get('name');
$user->pseudo = Request::get('pseudo');
$user->userId = Request::get('userId');
$user->hasFiat = Request::get('hasFiat');
$user->optin = Request::get('optin');
$user->mail = Request::get('mail');
$pseudo = Input::get('pseudo');
$userId = Input::get('userId');
$hasFiat = Input::get('hasFiat');
if($pseudo == '' || $hasFiat == '' )
{
return Response::json( array(
'status' => 'ko',
'message' => 'missing mandatory parameters')
);
}
else if($userId == '')
{
if( $user->save() )
{
$id = DB::table('user')
->where('pseudo','LIKE',$pseudo)
->pluck('userId');
return Response::json(array(
'status' => 'ok',
'message' => 'success',
'userId' => $id
));
}
else
{
return Response::json(array(
'message' => 'error while saving this user !!',
));
}
}
Laravel REST-ful (Resourceful) controlllers has pre-configured routes (can be re-configured):
According to : http://laravel.com/docs/controllers#resource-controllers
+-----------+---------------------------+---------+------------------+
| Verb | Path | Action | Route Name |
+-----------+---------------------------+---------+------------------+
| GET | /resource | index | resource.index |
| GET | /resource/create | create | resource.create |
| POST | /resource | store | resource.store |
| GET | /resource/{resource} | show | resource.show |
| GET | /resource/{resource}/edit | edit | resource.edit |
| PUT/PATCH | /resource/{resource} | update | resource.update |
| DELETE | /resource/{resource} | destroy | resource.destroy |
+-----------+---------------------------+---------+------------------+
Referencing the table each of the Verb must correspond to the action method in the controller.
For example if your Resourceful Route is registered as:
Route::resource('user', 'userInscriptionController');
Then to POST to user resource, you need to have userInscriptionController#store action (i.e. method called store() in your userInscriptionController.
To avoid manually creating each of these actions, you can use Laravel's artisan controller:make
php artisan controller:make userInscriptionController
which will generate all these actions for you, then you just need to fill in your logic to complete the resource.
From your comment, you are using
Route::resource('user', 'userInscriptionController');
which will generate following routes
Verb | Path | Action | Route Name
------------------------------------------------------------------------
GET | /resource | index | resource.index
GET | /resource/create | create | resource.create
POST | /resource | store | resource.store
GET | /resource/{resource} | show | resource.show
GET | /resource/{resource}/edit | edit | resource.edit
PUT/PATCH | /resource/{resource} | update | resource.update
DELETE | /resource/{resource} | destroy | resource.destroy
And as you can see, the only action allowing post is store. So you should use this one or add post route for an other method like this :
Route::post('your_url', array('as' => 'your_route_name', 'uses' => 'YourController#yourMethod'));
I hope it's clear now
if request is GET then:
if (Request::isMethod('get'))
{
$user = new userInscription;
$user->nom = Request::get('name');
$user->pseudo = Request::get('pseudo');
$user->userId = Request::get('userId');
$user->hasFiat = Request::get('hasFiat');
$user->optin = Request::get('optin');
$user->mail = Request::get('mail');
$pseudo = Input::get('pseudo');
$userId = Input::get('userId');
$hasFiat = Input::get('hasFiat');
if($pseudo == '' || $hasFiat == '' )
{
return Response::json( array(
'status' => 'ko',
'message' => 'missing mandatory parameters')
);
}
else if($userId == '')
{
if( $user->save() )
{
$id = DB::table('user')
->where('pseudo','LIKE',$pseudo)
->pluck('userId');
return Response::json(array(
'status' => 'ok',
'message' => 'success',
'userId' => $id
));
}
else
{
return Response::json(array(
'message' => 'error while saving this user !!',
));
}
}
}
===================
if request is POST then:
if (Request::isMethod('post'))
{
$user = new userInscription;
$user->nom = Request::post('name');
$user->pseudo = Request::post('pseudo');
$user->userId = Request::post('userId');
$user->hasFiat = Request::post('hasFiat');
$user->optin = Request::post('optin');
$user->mail = Request::post('mail');
$pseudo = Input::post('pseudo');
$userId = Input::post('userId');
$hasFiat = Input::post('hasFiat');
if($pseudo == '' || $hasFiat == '' )
{
return Response::json( array(
'status' => 'ko',
'message' => 'missing mandatory parameters')
);
}
else if($userId == '')
{
if( $user->save() )
{
$id = DB::table('user')
->where('pseudo','LIKE',$pseudo)
->pluck('userId');
return Response::json(array(
'status' => 'ok',
'message' => 'success',
'userId' => $id
));
}
else
{
return Response::json(array(
'message' => 'error while saving this user !!',
));
}
}
}