entityManager = $entityManager; $this->entity = $entity; $this->relationName = $relationName; $this->relationType = $entity->getRelationType($relationName); $this->entityType = $entity->getEntityType(); $this->foreignEntityType = $entity->getRelationParam($relationName, 'entity'); if ($query) { $this->builder = $this->createSelectBuilder()->clone($query); } else { $this->builder = $this->createSelectBuilder()->from($this->foreignEntityType); } } protected function createSelectBuilder() : SelectBuilder { return new SelectBuilder($this->entityManager->getQueryComposer()); } protected function getMapper() : Mapper { return $this->entityManager->getMapper(); } /** * Apply middle table conditions for a many-to-many relationship. * * Usage example: * `->columnsWhere(['column' => $value])` */ public function columnsWhere(array $where) : self { if ($this->relationType !== Entity::MANY_MANY) { throw new RuntimeException("Can't add columns where for not many-to-many relationship."); } $transformedWhere = $this->applyMiddleAliasToWhere($where); $this->where($transformedWhere); return $this; } protected function applyMiddleAliasToWhere(array $where) : array { $transformedWhere = []; $middleName = lcfirst( $this->entity->getRelationParam($this->relationName, 'relationName') ); foreach ($where as $key => $value) { $transformedKey = $key; $transformedValue = $value; if (is_int($key)) { $transformedKey = $key; } if ( is_string($key) && strlen($key) && strpos($key, '.') === false && $key[0] === strtolower($key[0]) ) { $transformedKey = $middleName . '.' . $key; } if (is_array($value)) { $transformedValue = $this->applyMiddleAliasToWhere($value); } $transformedWhere[$transformedKey] = $transformedValue; } return $transformedWhere; } /** * Find related records by a criteria. */ public function find() : Collection { $query = $this->builder->build(); $related = $this->getMapper()->selectRelated($this->entity, $this->relationName, $query); if ($related instanceof Collection) { return $related; } $collection = $this->entityManager->getCollectionFactory()->create($this->foreignEntityType); $collection->setAsFetched(); if ($related instanceof Entity) { $collection[] = $related; } return $collection; } /** * Find a first related records by a criteria. */ public function findOne() : ?Entity { $collection = $this->limit(0, 1)->find(); if (!count($collection)) { return null; } foreach ($collection as $entity) { return $entity; } } /** * Get a number of related records that meet criteria. */ public function count() : int { $query = $this->builder->build(); return $this->getMapper()->countRelated($this->entity, $this->relationName, $query); } /** * Add JOIN. * * @see Espo\ORM\QueryParams\SelectBuilder::join() */ public function join($relationName, ?string $alias = null, ?array $conditions = null) : self { $this->builder->join($relationName, $alias, $conditions); return $this; } /** * Add LEFT JOIN. * * @see Espo\ORM\QueryParams\SelectBuilder::leftJoin() */ public function leftJoin($relationName, ?string $alias = null, ?array $conditions = null) : self { $this->builder->leftJoin($relationName, $alias, $conditions); return $this; } /** * Set DISTINCT parameter. */ public function distinct() : self { $this->builder->distinct(); return $this; } /** * Set to return STH collection. Recommended for fetching large number of records. * * @todo Remove. */ public function sth() : self { $this->builder->sth(); return $this; } /** * Add a WHERE clause. * * @see Espo\ORM\QueryParams\SelectBuilder::where() * * @param array|string $keyOrClause * @param ?array|string $value */ public function where($keyOrClause = [], $value = null) : self { $this->builder->where($keyOrClause, $value); return $this; } /** * Add a HAVING clause. * * @see Espo\ORM\QueryParams\SelectBuilder::having() */ public function having($keyOrClause = [], $value = null) : self { $this->builder->having($keyOrClause, $params2); return $this; } /** * Apply ORDER. * * @see Espo\ORM\QueryParams\SelectBuilder::order() * * @param string|array $orderBy * @param bool|string $direction */ public function order($orderBy, $direction = 'ASC') : self { $this->builder->order($orderBy, $direction); return $this; } /** * Apply OFFSET and LIMIT. */ public function limit(?int $offset = null, ?int $limit = null) : self { $this->builder->limit($offset, $limit); return $this; } /** * Specify SELECT. Which attributes to select. All attributes are selected by default. * * @see Espo\ORM\QueryParams\SelectBuilder::select() * * @param array|string $select */ public function select($select, ?string $alias = null) : self { $this->builder->select($select, $alias); return $this; } /** * Specify GROUP BY. */ public function groupBy(array $groupBy) : self { $this->builder->groupBy($groupBy); return $this; } }