问题
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