Implemented search by tags with these operators: AND (&), OR (|)

Fixes #30
This commit is contained in:
Tim Schumacher 2014-10-15 17:23:12 +02:00
parent 7c2c17c571
commit 9921a789e5
2 changed files with 116 additions and 19 deletions

View file

@ -25,6 +25,7 @@ use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\CalendarExport;
use Symfony\Component\Validator\Constraints\DateTime;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
/**
* Tag controller.
@ -47,26 +48,98 @@ class TagController extends Controller
/** @var EntityRepository $repo */
$repo = $em->getRepository('CalciferBundle:Tag');
$tags = [];
$operator = 'or';
if (strpos($slug,'|') !== false) {
$slugs = explode('|',$slug);
foreach($slugs as $item) {
/** @var Tag $tag */
$tag = $repo->findOneBy(['slug' => $item]);
/** @var Tag $location */
if ($tag instanceof Tag) {
$tags[] = $tag;
}
}
} else if (strpos($slug,'&') !== false) {
$slugs = explode('&',$slug);
$operator = 'and';
foreach($slugs as $item) {
/** @var Tag $tag */
$tag = $repo->findOneBy(['slug' => $item]);
if ($tag instanceof Tag) {
$tags[] = $tag;
}
}
} else {
/** @var Tag $tag */
$tag = $repo->findOneBy(['slug' => $slug]);
if (!$tag) {
if ($tag instanceof Tag) {
$tags[] = $tag;
}
}
if (count($tags) == 0) {
throw $this->createNotFoundException('Unable to find tag entity.');
}
$now = new \DateTime();
$now->setTime(0, 0, 0);
$entities = null;
if ($operator == 'and') {
$sql = <<<EOF
SELECT * FROM events AS e
WHERE id IN (
WITH events_on_tags AS (
SELECT events_id, array_agg(tags_id) as tags
FROM events2tags
GROUP BY events_id
)
SELECT events_id FROM events_on_tags
WHERE tags @> array[@tags@]
)
AND e.startdate >= :startdate
ORDER BY e.startdate
EOF;
$tag_ids = array_reduce($tags,function($carry,$item){
if (strlen($carry) == 0) {
return $item->id;
} else {
return $carry . ',' . $item->id;
}
});
$sql = str_replace('@tags@',$tag_ids,$sql);
$rsm = new ResultSetMappingBuilder($em);
$rsm->addRootEntityFromClassMetadata('CalciferBundle:Event','e');
$query = $em->createNativeQuery($sql,$rsm);
$query->setParameter('startdate',$now);
$entities = $query->getResult();
} else {
/** @var QueryBuilder $qb */
$qb = $em->createQueryBuilder();
$qb->select(array('e'))
->from('CalciferBundle:Event', 'e')
->join('e.tags', 't', 'WITH', $qb->expr()->in('t.id', $tag->id))
->where('e.startdate >= :startdate')
->orderBy('e.startdate')
->setParameter('startdate', $now);
$qb->join('e.tags', 't', 'WITH', $qb->expr()->in('t.id', array_reduce($tags,function($carry,$item){
if (strlen($carry) == 0) {
return $item->id;
} else {
return $carry . ',' . $item->id;
}
})));
$entities = $qb->getQuery()->execute();
}
if ($format == 'ics') {
$calendar = new Calendar();
@ -110,7 +183,8 @@ class TagController extends Controller
} else {
return array(
'entities' => $entities,
'tag' => $tag,
'tags' => $tags,
'operator' => $operator,
);
}
}

View file

@ -23,7 +23,22 @@
<div class="ui column">
<h1>
Termine
{% if tag|default(false) %} für Tag „{{ tag.name }}{% endif %}
{% if tags|default(false) %}
{% if tags|length == 1 %}
für Tag {{ tags[0].name }}
{% elseif(tags|length == 2) %}
für die Tags {{ tags[0].name }} {% if operator == 'or' %}oder{% else %}und{% endif %} {{ tags[1].name }}
{% else %}
für die Tags
{% for tag in tags %}
{% if not loop.last %}
{{ tag.name }}{% if loop.index < (tags|length - 1) %},{% endif %}
{% else %}
{% if operator == 'or' %}oder{% else %}und{% endif %} {{ tag.name }}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% if location|default(false) %} für Ort „{{ location.name }}“ <a class="location-edit" href="{{ path("location_edit",{slug:location.slug}) }}"><i class="ui icon edit inverted green circular link" data-content="Ort bearbeiten" title="Ort bearbeiten"></i> </a> {% endif %}
</h1>
{% if tag|default(false) %}
@ -68,10 +83,18 @@
</div>
</div>
{% if entities|length > 0 %}
<div class="ui three column page grid stackable">
{% for entity in entities %}
{{ include('CalciferBundle:Event:event_box.html.twig',{'truncate_summary':true}) }}
{% endfor %}
</div>
{% else %}
<div class="ui one column page grid stackable">
<div class="ui column">
<p>Es konnten keine Termine gefunden werden.</p>
</div>
</div>
{% endif %}
{% endblock %}