/**
* Executes each db query needed
*
* Note: The {$alias} property is set two times because Phalcon Model ignores
* empty arrays when overloading property set.
*
* Also {@see https://github.com/stibiumz/phalcon.eager-loading/issues/1}
*
* @return $this
*/
public function load()
{
$relation = $this->relation;
$alias = $relation->getOptions();
$alias = strtolower($alias['alias']);
$relField = $relation->getFields();
$relReferencedModel = $relation->getReferencedModel();
$relReferencedField = $relation->getReferencedFields();
$relIrModel = $relation->getIntermediateModel();
$relIrField = $relation->getIntermediateFields();
$relIrReferencedField = $relation->getIntermediateReferencedFields();
// PHQL has problems with this slash
if ($relReferencedModel[0] === '\\') {
$relReferencedModel = ltrim($relReferencedModel, '\\');
}
$bindValues = array();
foreach ($this->parent->getSubject() as $record) {
$bindValues[$record->readAttribute($relField)] = true;
}
$bindValues = array_keys($bindValues);
$subjectSize = count($this->parent->getSubject());
$isManyToManyForMany = false;
$builder = new QueryBuilder();
$builder->from($relReferencedModel);
if ($isThrough = $relation->isThrough()) {
if ($subjectSize === 1) {
// The query is for a single model
$builder->innerJoin($relIrModel, sprintf('[%s].[%s] = [%s].[%s]', $relIrModel, $relIrReferencedField, $relReferencedModel, $relReferencedField))->inWhere("[{$relIrModel}].[{$relIrField}]", $bindValues);
} else {
// The query is for many models, so it's needed to execute an
// extra query
$isManyToManyForMany = true;
$relIrValues = new QueryBuilder();
$relIrValues = $relIrValues->from($relIrModel)->inWhere("[{$relIrModel}].[{$relIrField}]", $bindValues)->getQuery()->execute()->setHydrateMode(Resultset::HYDRATE_ARRAYS);
$bindValues = $modelReferencedModelValues = array();
foreach ($relIrValues as $row) {
$bindValues[$row[$relIrReferencedField]] = true;
$modelReferencedModelValues[$row[$relIrField]][$row[$relIrReferencedField]] = true;
}
unset($relIrValues, $row);
$builder->inWhere("[{$relReferencedField}]", array_keys($bindValues));
}
} else {
$builder->inWhere("[{$relReferencedField}]", $bindValues);
}
if ($this->constraints) {
call_user_func($this->constraints, $builder);
}
$records = array();
if ($isManyToManyForMany) {
foreach ($builder->getQuery()->execute() as $record) {
$records[$record->readAttribute($relReferencedField)] = $record;
}
foreach ($this->parent->getSubject() as $record) {
$referencedFieldValue = $record->readAttribute($relField);
if (isset($modelReferencedModelValues[$referencedFieldValue])) {
$referencedModels = array();
foreach ($modelReferencedModelValues[$referencedFieldValue] as $idx => $_) {
$referencedModels[] = $records[$idx];
}
$record->{$alias} = $referencedModels;
if (static::$isPhalcon2) {
$record->{$alias} = null;
$record->{$alias} = $referencedModels;
}
} else {
$record->{$alias} = null;
$record->{$alias} = array();
}
}
$records = array_values($records);
} else {
// We expect a single object or a set of it
$isSingle = !$isThrough && ($relation->getType() === Relation::HAS_ONE || $relation->getType() === Relation::BELONGS_TO);
if ($subjectSize === 1) {
// Keep all records in memory
foreach ($builder->getQuery()->execute() as $record) {
$records[] = $record;
}
$record = $this->parent->getSubject();
$record = $record[0];
if ($isSingle) {
$record->{$alias} = empty($records) ? null : $records[0];
} else {
if (empty($records)) {
$record->{$alias} = null;
$record->{$alias} = array();
} else {
$record->{$alias} = $records;
if (static::$isPhalcon2) {
$record->{$alias} = null;
$record->{$alias} = $records;
}
}
}
} else {
$indexedRecords = array();
// Keep all records in memory
foreach ($builder->getQuery()->execute() as $record) {
$records[] = $record;
if ($isSingle) {
$indexedRecords[$record->readAttribute($relReferencedField)] = $record;
} else {
$indexedRecords[$record->readAttribute($relReferencedField)][] = $record;
}
}
foreach ($this->parent->getSubject() as $record) {
$referencedFieldValue = $record->readAttribute($relField);
if (isset($indexedRecords[$referencedFieldValue])) {
$record->{$alias} = $indexedRecords[$referencedFieldValue];
if (static::$isPhalcon2 && is_array($indexedRecords[$referencedFieldValue])) {
$record->{$alias} = null;
$record->{$alias} = $indexedRecords[$referencedFieldValue];
}
} else {
$record->{$alias} = null;
if (!$isSingle) {
$record->{$alias} = array();
}
}
}
}
}
$this->subject = $records;
return $this;
}