<?php
namespace Products\NotificationsBundle\Entity\Lists;
use App\Model\Query\ConditionQuery\Condition\ContainsCondition;
use App\Model\Query\ConditionQuery\Condition\OverlapsCondition;
use App\Model\Query\ConditionQuery\ConditionGroup;
use App\Model\Query\ConditionQuery\ConditionGroupInterface;
use App\Model\Query\ConditionQuery\ConditionQuery;
use App\Model\Query\ConditionQuery\ConditionQueryListInterface;
use Cms\CoreBundle\Entity\OneRoster\OneRosterUser;
use Doctrine\ORM\Mapping as ORM;
use Products\NotificationsBundle\Entity\AbstractList;
use Products\NotificationsBundle\Util\ListBuilder\AbstractListBuilder;
/**
* @ORM\Entity(
* repositoryClass = "Products\NotificationsBundle\Doctrine\Repository\Lists\ConditionListRepository",
* )
*/
class ConditionList extends AbstractList implements ConditionQueryListInterface
{
public const DISCR = 'condition';
/**
* @var ConditionQuery
*
* @ORM\Column(
* type = "condition_query",
* nullable = false,
* )
*/
protected ConditionQuery $conditionQuery;
/**
* @param ConditionQuery|null $conditionQuery
*/
public function __construct(?ConditionQuery $conditionQuery = null)
{
$this->conditionQuery = $conditionQuery ?? new ConditionQuery();
}
/**
* @return ConditionQuery
*/
public function getRunnableConditionQuery(): ConditionQuery
{
if ( ! $this->getTypes()) {
return $this->decorateSchool($this->getConditionQuery());
}
if ( ! $this->getConditionQuery()->getConditions()) {
return $this->decorateSchool($this->getConditionQuery());
}
return ($this->decorateSchool(new ConditionQuery(
$this->getConditionQuery()->getEntity(),
[
new OverlapsCondition(
sprintf(
'%s.metadata/_role_type',
ConditionQuery::ENTITY_ALIAS_MAP[ConditionQuery::PROFILE_ENTITY],
),
$this->getTypeNames()
),
new ConditionGroup(
$this->getConditionQuery()->getConditions(),
$this->getConditionQuery()->getMode(),
),
],
ConditionGroupInterface::MODES__AND
)))
->setEntityOverride($this->getConditionQuery()->hasEntityOverride())
->setConditionConfig($this->getConditionQuery()->getConditionConfig())
->setConditionContext($this);
}
/**
* {@inheritDoc}
*/
public function getConditionQuery(): ConditionQuery
{
return $this->conditionQuery;
}
/**
* @param ConditionQuery $conditionQuery
* @return ConditionList
*/
public function setConditionQuery(ConditionQuery $conditionQuery): self
{
$this->conditionQuery = $conditionQuery->setEntityOverride(
$this->getConditionQuery()->hasEntityOverride(),
);
return $this;
}
/**
* @return bool
*/
public function isEntityOverridable(): bool
{
return $this->getTypes() === OneRosterUser::TYPES__FAMILY;
}
/**
* {@inheritDoc}
*/
public static function factory(?string $variant = null): AbstractList
{
if ( ! $variant) {
throw new \RuntimeException();
}
$list = new self();
switch (true) {
case $variant === 'family':
$list
->setTypes(OneRosterUser::TYPES__FAMILY)
->setConditionQuery(
new ConditionQuery(ConditionQuery::PROFILE_ENTITY),
);
break;
case $variant === 'staff':
$list
->setTypes(OneRosterUser::TYPES__STAFF)
->setConditionQuery(
new ConditionQuery(ConditionQuery::PROFILE_ENTITY),
);
break;
case $variant === 'community':
$list
->setTypes(OneRosterUser::TYPES__COMMUNITY)
->setConditionQuery(
new ConditionQuery(ConditionQuery::PROFILE_ENTITY),
);
break;
case $variant === 'student':
$list
->setTypes(OneRosterUser::TYPES__STUDENT)
->setConditionQuery(
new ConditionQuery(ConditionQuery::PROFILE_ENTITY),
);
break;
default:
throw new \RuntimeException();
}
return $list;
}
/**
* @return string
*/
public function getEntity(): string
{
return $this->getConditionQuery()->getEntity(true);
}
/**
* @return string
*/
public function getEntityClass(): string
{
return $this->getConditionQuery()->getEntityClass();
}
/**
* @return array<string>
*/
public function getFilterableEntities(): array
{
return array_values(array_filter([
AbstractListBuilder::ENTITIES__PROFILES,
$this->isEntityOverridable() ? AbstractListBuilder::ENTITIES__PROFILE_RELATIONSHIPS : null,
$this->isEntityOverridable() ? AbstractListBuilder::ENTITIES__STUDENTS : null,
]));
}
/**
* @param ConditionQuery $conditionQuery
* @return ConditionQuery
*/
private function decorateSchool(ConditionQuery $conditionQuery): ConditionQuery
{
if ( ! $this->getSchool()) {
return $conditionQuery;
}
// if the school is a district level, we don't need to modify the query in any way
// the district school should cover the entire database
// we only care about filtering the list further if we have a non-district school
if ($this->getSchool()->isTypeDistrict()) {
return $conditionQuery;
}
// determine the entity we want to enforce the school checking on
// if we are building a staff or community list, we want to check against the profiles directly
// if we are doing a parent list, we really want to cross-check against the students
// NOTE: this assumes only a single type is allowed (as is currently coded 2024-05-20)
// if the system ever allows for mixed types when making a list, this logic will need changed...
$entity = $conditionQuery->getEntity();
if ($this->hasType(OneRosterUser::TYPES__FAMILY)) {
$entity = ConditionQuery::STUDENT_ENTITY;
}
return new ConditionQuery(
$conditionQuery->getEntity(),
[
new ContainsCondition(
ConditionQuery::ENTITY_ALIAS_MAP[$entity] . '.metadata/_orgs',
$this->getSchool()->getOneRosterOrg()
),
new ConditionGroup(
$this->getConditionQuery()->getConditions(),
$this->getConditionQuery()->getMode(),
),
],
ConditionGroupInterface::MODES__AND
);
}
}