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.
Related
I'm working on a project with Laravel and Eloquent/MySQL.
I'd like to know how to handle a many-to-many relationship with three tables (users, merchants, roles).
Any user can have one or more merchant.
Any merchant can be shared between users.
Any user have a specific role for a merchant.
Is there a best practice to follow?
How can I get all the merchants by a user having a specific role?
Thanks for helping
This is my current structure:
Users
-------------------------------
| id | first_name | last_name |
-------------------------------
Merchants
-------------------
| id | trade_name |
-------------------
Roles
-------------------------
| id | name | hierarchy |
-------------------------
merchant_user_role
-----------------------------------
| merchant_id | user_id | role_id |
-----------------------------------
Merchant Model
<?php
class Merchant extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'merchant_user_role')
->withPivot('role_id')
->withTimestamps()
->orderBy('first_name', 'asc');
}
}
User Model
<?php
class User extends Model
{
public function merchants()
{
return $this->belongsToMany('App\Merchant', 'merchant_user_role')
->withPivot('role_id')
->withTimestamps()
->orderBy('trade_name', 'asc');
}
public function roles()
{
return $this->belongsToMany('App\Role', 'merchant_user_role')
->withTimestamps();
}
}
Role Model
<?php
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User')->withTimestamps();
}
}
$merchants = $user->merchants()->where('role_id', $some_role_id)->get();
I have a json field in my database name is permissions {"read": true, "create": true}. Now, I want to show this in my blade view. I had try this way but it's show an error.
#foreach($users as $user)
<tr>
<td>{{$user->roles->permissions}}</td>
</tr>
#endforeach
show this Error Message.
Property [permissions] does not exist on this collection instance.
User Model
public function roles()
{
return $this->belongsToMany(Role::class,'user_roles');
}
I tested this scenario and was OK . i hope usefull for you :
my route :
Route::get('/test', function (Request $request) {
$users=\App\User::with('roles')->get();
return view('welcome',compact('users'));
});
define roles method inside user model :
public function roles()
{
return $this->belongsToMany(Role::class,'user_roles');;
}
and get permissions in view :
#foreach($users as $user)
#foreach($user->roles as $role)
<tr>
<td>{{$role}}</td>
</tr>
#endforea
#endforeach
I test with these tables
user_roles
user_id | role_id
-----------------
1 | 2
1 | 2
roles:
id | permissions
-----------------
1 | {"read": true, "create": true}
2 | {"read": true, "create": false}
users:
id | name | email | password | remember_token | created_at|updated_at
----------------------------------------------------------------------
1 | alihossein|ali#gmail.com|XXXXX|UIWKK67ei5CCuiv1OXilKY2aRkTfSqGLpqJch0F9YmenGSorsQGHVvWiX6kP| 2018-05-28 22:25:14 | 2018-05-28 22:25:14
I am new in cakephp and my table like:
city
id | name
1 | city1
2 | city2
state
id | name | cityid
1 |state1| 2
so how do i get the city name if i having state id.
In controller i have code like this.
public function getCity()
{
if( $this->request->is('ajax') ) {
$this->autoRender = false;
}
if ($this->request->isPost()) {
$sId= $this->request->data['stateid'];
}
}
In the $sId i get value so how do i write query.
If you have a BelongsTo relationship between both Model, you just have to do a query on the States which contains City:
public function getCity()
{
if( $this->request->is('ajax') ) {
$this->autoRender = false;
}
if ($this->request->isPost()) {
$stateEntity = $this->States->find('all')
->where(['id' => $this->request->data['stateid']])
->contain(['Cities'])
->first();
// Now the State Object contains City
$cityName = $stateEntity->city->name;
}
}
To create this relationship you need to do like this:
class StatesTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Cities')
->setForeignKey('city_id')
->setJoinType('INNER');
}
}
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
}
}
I have one games table and two tables to store their scores, game_scores and thirdparty_game_scores with exactly the same structure, something like that:
-------------------------GAMES-------------------------
slug | title | technology | (other non-relevant fields)
-------------------------------------------------------
----------------------GAME SCORES----------------------
id (PK) | game_slug | user | team | score | timestamp
-------------------------------------------------------
----------------THIRDPARTY GAME SCORES-----------------
id (PK) | game_slug | user | team | score | timestamp
-------------------------------------------------------
And their Models like:
class GameScores extends BaseGamesModel {
public function initialize() {
parent::initialize();
$this->belongsTo( 'game_slug', 'Games', 'slug' );
$this->skipAttributes(array('creation_time', 'last_modified'));
}
}
I'm building a weekly ranking with game scores stored only on game_scores:
$ranking = GameScores::sum(
array(
"column" => "score",
"conditions" => $conditions,
"group" => "team",
"order" => "sumatory DESC"
)
);
Is there any way to create a Model that concatenates all data from game_scores and thirdparty_game_scores to create the same ranking without any extra effort?
Thank you so much!
You can set the source table dynamically in your model. See Can I build a Model for two Tables on PhalconPHP?
You should use the setSource() method and put your condition in there, e.g.:
$source = 'default_table';
if (class_name($this) === 'A') {
$source = 'A_table';
}
return $source;