Skip to content
This repository was archived by the owner on May 14, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ composer.lock
.*.sw[a-z]
*.un~
Session.vim
/nbproject/private/
nbproject/project.properties
nbproject/project.xml
153 changes: 108 additions & 45 deletions Datatables/Datatable.php
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/**
* Recognizes mData sent from DataTables where dotted notations represent a related
* entity. For example, defining the following in DataTables...
Expand Down Expand Up @@ -30,13 +31,13 @@
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;

use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\HttpFoundation\Response;

class Datatable
{

/**
* Doctrine innerJoin type
*/
Expand Down Expand Up @@ -193,6 +194,11 @@ class Datatable
* @var array The formatted data from the search results to return to DataTables.js
*/
protected $datatable;
/**
*
* @var integer Used to add associations multiple times if required
*/
protected $count = 1;

public function __construct(array $request, EntityRepository $repository, ClassMetadata $metadata, EntityManager $em, $serializer)
{
Expand All @@ -207,7 +213,7 @@ public function __construct(array $request, EntityRepository $repository, ClassM
$this->setParameters();
$this->qb = $em->createQueryBuilder();
$this->echo = $this->request['sEcho'];
$this->search = $this->request['sSearch'];
$this->search = isset($this->request['sSearch']) ? : '';
$this->offset = $this->request['iDisplayStart'];
$this->amount = $this->request['iDisplayLength'];

Expand Down Expand Up @@ -271,7 +277,7 @@ public function setParameters()
if (is_numeric($this->request['iColumns'])) {
$params = array();
$associations = array();
for ($i=0; $i < intval($this->request['iColumns']); $i++) {
for ($i = 0; $i < intval($this->request['iColumns']); $i++) {
$fields = explode('.', $this->request['mDataProp_' . $i]);
$params[] = $this->request['mDataProp_' . $i];
$associations[] = array('containsCollections' => false);
Expand All @@ -293,7 +299,8 @@ public function setParameters()
* @param array Association information for a column (by reference)
* @param array The column fields from dotted notation
*/
protected function setRelatedEntityColumnInfo(array &$association, array $fields) {
protected function setRelatedEntityColumnInfo(array &$association, array $fields, $multipleJoin = false)
{
$mdataName = implode('.', $fields);
$lastField = Container::camelize(array_pop($fields));
$joinName = $this->tableName;
Expand All @@ -311,33 +318,37 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields
$association['containsCollections'] = true;
}
$metadata = $this->em->getClassMetadata(
$metadata->getAssociationTargetClass($entityName)
$metadata->getAssociationTargetClass($entityName)
);

if ($metadata->hasField(lcfirst($lastField))) {
$association['type'] = $metadata->getTypeOfField(lcfirst($lastField));
}

$joinName .= '_' . $this->getJoinName(
$metadata,
Container::camelize($metadata->getTableName()),
$entityName
$metadata, Container::camelize($metadata->getTableName()), $entityName
);
// The join required to get to the entity in question
if ($multipleJoin) {
$joinName .= '_' . $this->count++;
}
error_log($joinName);
if (!isset($this->assignedJoins[$joinName])) {
$this->assignedJoins[$joinName]['joinOn'] = $joinOn;
$this->assignedJoins[$joinName]['mdataColumn'] = $columnName;
$this->identifiers[$joinName] = $metadata->getIdentifierFieldNames();
}
}
else {
} else {
throw new Exception(
"Association '$entityName' not found ($mdataName)",
'404'
"Association '$entityName' not found ($mdataName)", '404'
);
}
}

// Check the last field on the last related entity of the dotted notation
if (!$metadata->hasField(lcfirst($lastField))) {
throw new Exception(
"Field '$lastField' on association '$entityName' not found ($mdataName)",
'404'
"Field '$lastField' on association '$entityName' not found ($mdataName)", '404'
);
}
$association['entityName'] = $entityName;
Expand All @@ -352,19 +363,54 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields
* @param array The association information as a reference
* @param string The field name on the main entity
*/
protected function setSingleFieldColumnInfo(array &$association, $fieldName) {
protected function setSingleFieldColumnInfo(array &$association, $fieldName)
{
$fieldName = Container::camelize($fieldName);

if (!$this->metadata->hasField(lcfirst($fieldName))) {
throw new Exception(
"Field '$fieldName' not found.)",
'404'
"Field '$fieldName' not found.)", '404'
);
}

$association['fieldName'] = $fieldName;
$association['entityName'] = $this->tableName;
$association['fullName'] = $this->tableName . '.' . lcfirst($fieldName);
$association['type'] = $this->metadata->getTypeOfField(lcfirst($fieldName));
}

/**
* reverses and hyphens date time string
* @param strng $str
* @return string
*/
private function formatDateForDb($str)
{

// allow for hyphens instead of slashes
$str = str_replace('-', '/', trim($str));

// no hyphens - nothing to do
if (false === strpos($str, '/')) {
return $str;
}

// extract date time
$parts = preg_split('/ /', $str);

// reverse date part
$a = explode('/', $parts[0]);
$a = array_reverse($a);

// glue back together with hyphens
$out = implode('-', $a);

// add time part back on
if (count($parts) > 1) {
$out .= ' ' . $parts[1];
}

return $out;
}

/**
Expand All @@ -380,7 +426,7 @@ protected function getJoinName(ClassMetadata $metadata, $tableName, $entityName)

// If it is self-referencing then we must avoid collisions
if ($metadata->getName() == $this->metadata->getName()) {
$joinName .= "_$entityName";
$joinName .= "_$entityName";
}

return $joinName;
Expand Down Expand Up @@ -457,10 +503,9 @@ public function setOrderBy(QueryBuilder $qb)
{
if (isset($this->request['iSortCol_0'])) {
for ($i = 0; $i < intval($this->request['iSortingCols']); $i++) {
if ($this->request['bSortable_'.intval($this->request['iSortCol_'. $i])] == "true") {
if ($this->request['bSortable_' . intval($this->request['iSortCol_' . $i])] == "true") {
$qb->addOrderBy(
$this->associations[$this->request['iSortCol_'.$i]]['fullName'],
$this->request['sSortDir_'.$i]
$this->associations[$this->request['iSortCol_' . $i]]['fullName'], $this->request['sSortDir_' . $i]
);
}
}
Expand All @@ -477,29 +522,31 @@ public function setWhere(QueryBuilder $qb)
// Global filtering
if ($this->search != '') {
$orExpr = $qb->expr()->orX();
for ($i=0 ; $i < count($this->parameters); $i++) {
if (isset($this->request['bSearchable_'.$i]) && $this->request['bSearchable_'.$i] == "true") {
for ($i = 0; $i < count($this->parameters); $i++) {
if (isset($this->request['bSearchable_' . $i]) && $this->request['bSearchable_' . $i] == "true") {
$qbParam = "sSearch_global_{$this->associations[$i]['entityName']}_{$this->associations[$i]['fieldName']}";
$orExpr->add($qb->expr()->like(
$this->associations[$i]['fullName'],
":$qbParam"
$this->associations[$i]['fullName'], ":$qbParam"
));
$qb->setParameter($qbParam, "%" . $this->request['sSearch'] . "%");
if('datetime' == $this->associations[$i]['type']) {
$qb->setParameter($qbParam, "%" . $this->formatDateForDb($this->request['sSearch']) . "%");
} else {
$qb->setParameter($qbParam, "%" . $this->request['sSearch'] . "%");
}
}
}
$qb->where($orExpr);
}

// Individual column filtering
$andExpr = $qb->expr()->andX();
for ($i=0 ; $i < count($this->parameters); $i++) {
if (isset($this->request['bSearchable_'.$i]) && $this->request['bSearchable_'.$i] == "true" && $this->request['sSearch_'.$i] != '') {
for ($i = 0; $i < count($this->parameters); $i++) {
if (isset($this->request['bSearchable_' . $i]) && $this->request['bSearchable_' . $i] == "true" && $this->request['sSearch_' . $i] != '') {
$qbParam = "sSearch_single_{$this->associations[$i]['entityName']}_{$this->associations[$i]['fieldName']}";
$andExpr->add($qb->expr()->like(
$this->associations[$i]['fullName'],
":$qbParam"
$this->associations[$i]['fullName'], ":$qbParam"
));
$qb->setParameter($qbParam, "%" . $this->request['sSearch_'.$i] . "%");
$qb->setParameter($qbParam, "%" . $this->request['sSearch_' . $i] . "%");
}
}
if ($andExpr->count() > 0) {
Expand All @@ -513,6 +560,19 @@ public function setWhere(QueryBuilder $qb)
}
}

/**
* Adds a manual association
*
* @param type $name - the dotted notation like in mData of the field you need adding
*/
public function addManualAssociation($name, $multipleJoin = false)
{
$newAssociation = array('containsCollections' => false);
$fields = explode('.', $name);
$this->setRelatedEntityColumnInfo($newAssociation, $fields, $multipleJoin);
$this->associations[] = $newAssociation;
}

/**
* Configure joins for entity associations
*
Expand All @@ -522,7 +582,7 @@ public function setAssociations(QueryBuilder $qb)
{
foreach ($this->assignedJoins as $joinName => $joinInfo) {
$joinType = isset($this->joinTypes[$joinInfo['mdataColumn']]) ?
$this->joinTypes[$joinInfo['mdataColumn']] : $this->defaultJoinType;
$this->joinTypes[$joinInfo['mdataColumn']] : $this->defaultJoinType;
call_user_func_array(array($qb, $joinType . 'Join'), array(
$joinInfo['joinOn'],
$joinName
Expand Down Expand Up @@ -576,7 +636,7 @@ public function setSelect(QueryBuilder $qb)
* Method to execute after constructing this object. Configures the object before
* executing getSearchResults()
*/
public function makeSearch()
public function makeSearch()
{
$this->setSelect($this->qb);
$this->setAssociations($this->qb);
Expand All @@ -594,8 +654,9 @@ public function makeSearch()
* @param array An arrray to check
* @return bool true if associative
*/
protected function isAssocArray(array $array) {
return (bool)count(array_filter(array_keys($array), 'is_string'));
protected function isAssocArray(array $array)
{
return (bool) count(array_filter(array_keys($array), 'is_string'));
}

/**
Expand All @@ -607,7 +668,7 @@ public function executeSearch()

$query = $this->qb->getQuery()->setHydrationMode(Query::HYDRATE_ARRAY);
$items = $this->useDoctrinePaginator ?
new Paginator($query, $this->doesQueryContainCollections()) : $query->execute();
new Paginator($query, $this->doesQueryContainCollections()) : $query->execute();

foreach ($items as $item) {
if ($this->useDtRowClass && !is_null($this->dtRowClass)) {
Expand All @@ -617,7 +678,7 @@ public function executeSearch()
$item['DT_RowId'] = $item[$this->rootEntityIdentifier];
}
// Go through each requested column, transforming the array as needed for DataTables
for ($i = 0 ; $i < count($this->parameters); $i++) {
for ($i = 0; $i < count($this->parameters); $i++) {
// Results are already correctly formatted if this is the case...
if (!$this->associations[$i]['containsCollections']) {
continue;
Expand Down Expand Up @@ -690,8 +751,7 @@ public function getSearchResults($resultType = '')
{
if (empty($resultType) || !defined('self::RESULT_' . strtoupper($resultType))) {
$resultType = $this->defaultResultType;
}
else {
} else {
$resultType = constant('self::RESULT_' . strtoupper($resultType));
}

Expand Down Expand Up @@ -736,17 +796,18 @@ public function getSearchResultsResponse()
public function getCountAllResults()
{
$qb = $this->repository->createQueryBuilder($this->tableName)
->select('count(' . $this->tableName . '.' . $this->rootEntityIdentifier . ')');
->select('count(' . $this->tableName . '.' . $this->rootEntityIdentifier . ')');
$this->setAssociations($qb);

if (!empty($this->callbacks['WhereBuilder']) && $this->hideFilteredCount) {
if (!empty($this->callbacks['WhereBuilder']) && $this->hideFilteredCount) {
foreach ($this->callbacks['WhereBuilder'] as $callback) {
$callback($qb);
}
}

return (int) $qb->getQuery()->getSingleScalarResult();
}

/**
* @return int Total query results after searches/filtering
*/
Expand All @@ -762,7 +823,8 @@ public function getCountFilteredResults()
/**
* @param object A callback function to be used at the end of 'setWhere'
*/
public function addWhereBuilderCallback($callback) {
public function addWhereBuilderCallback($callback)
{
if (!is_callable($callback)) {
throw new \Exception("The callback argument must be callable.");
}
Expand All @@ -788,11 +850,12 @@ public function getAmount()

public function getSearch()
{
return "%" . $this->search . "%";
return "%" . $this->search . "%";
}

public function getQueryBuilder()
{
return $this->qb;
return $this->qb;
}

}
Loading