问题
I'm building a REST API for the output of endpoint /projects I have created 2 models:
Projects:
class Projects extends BaseModel
{
public function initialize()
{
$this->hasMany('id', 'Participants', 'projectId');
}
}
Participants:
class Participants extends BaseModel
{
public function initialize()
{
$this->belongsTo('projectId', 'Projects', 'id');
}
}
Lets say, I have 10 projects: (1 query)
$results = Projects::find();
I loop through all 10 of them, but I want all Participants too:
foreach($results as $result) {
echo $result->participants; // 1 query
}
So at the end of the loop Phalcon has made an extra query for each project.
These queries were made by accessing $result->participants while iterating over 10 projects:
SELECT IF(COUNT(*)>0, 1 , 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`='projects'
DESCRIBE `projects`
SELECT `projects`.`id`, `projects`.`title`, `projects`.`client`, `projects`.`color`, `projects`.`start_date`, `projects`.`end_date`, `projects`.`notes`, `projects`.`stateId`, `projects`.`created_at`, `projects`.`updated_at` FROM `projects`
SELECT IF(COUNT(*)>0, 1 , 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`='project_participants'
DESCRIBE `project_participants`
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
SELECT `project_participants`.`id`, `project_participants`.`project_id`, `project_participants`.`user_id`, `project_participants`.`user_role_id`, `project_participants`.`user_state_id`, `project_participants`.`updated_at`, `project_participants`.`created_at` FROM `project_participants` WHERE `project_participants`.`project_id` = :0
Question
Is there a way to query the relations before hand, so it will be one query. When I use the Query Builder provided by Phalcon I cannot access ->participants the same way.
Edit
I ended up using Query Builder, namespacing all columns
$builder = $modelsManager->createBuilder();
$builder->columns($columns)
->from('Projects')
->leftJoin('Participants')
->getQuery()
->execute();
Columns like so:
Projects.id as projects_id
...
Participants.id as participants_id
Participants.projectId as participants_projectId
Because accessing ->participants on the result created by the Query Builder, did extra queries too.
回答1:
To access ->participants the same way using QueryBuilder, you will have to build join into Query.
Code example could be something like:
$queryBuilder = $this->getDI()->getModelsManager()
->createBuilder()
->columns(['p.id','participants.*'])
->addFrom('Entity\Projects', 'p')
->leftJoin('Entity\Participants', 'participants.projectId = p.id', 'participants')
->groupBy('p.id, participants.id')
->orderBy('p.id ASC');
$resultSet = $queryBuilder->getQuery()->execute();
groupBy()
by is used here for making result possibly multi-dimensional.
That kind of query (tested under PgSQL) made Phalcon create some subsequent ResultSet objects of participants pi
inside Resultsets for projects p
.
You still can iterate through it by using foreach()
but after all, I'am not sure it did reduce final query count.
Fireing $result = $resultSet->toArray()
made $result['pi']
remain as Resultset, so u should stay cautious on that. You may force it to dump as arrays by defining exact columns in columns()
parameters. It has its downside - you will no longer profit from groupBy()
, at least on Phalcon 1.3.2 and PHP 5.5.3 im running here.
回答2:
There is a great library for eager loading on phalcon.
stibiumz phalcon eager loading
That library solves the N + 1 queries for relationships. It is already included on the phalcon incubator. I'm using it on production already.
What it does is create a query using the IN clause and filling the models with the results.
On a has many it does:
SELECT * FROM main
SELECT * FROM related WHERE x.id IN (results from the previous resultset)
来源:https://stackoverflow.com/questions/28755079/phalcon-performance-related-queries