From 6c76076d8d6bda3f70d648e7d0439946cd165504 Mon Sep 17 00:00:00 2001 From: manseuk Date: Wed, 7 Aug 2013 13:36:57 +0100 Subject: [PATCH 1/8] Added associations to count query Added the associations to the count query so that you can add where callbacks to associated columns. --- Datatables/Datatable.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Datatables/Datatable.php b/Datatables/Datatable.php index a65fdf1..80fd8d0 100755 --- a/Datatables/Datatable.php +++ b/Datatables/Datatable.php @@ -737,6 +737,7 @@ public function getCountAllResults() { $qb = $this->repository->createQueryBuilder($this->tableName) ->select('count(' . $this->tableName . '.' . $this->rootEntityIdentifier . ')'); + $this->setAssociations($qb); if (!empty($this->callbacks['WhereBuilder']) && $this->hideFilteredCount) { foreach ($this->callbacks['WhereBuilder'] as $callback) { From ea940e80cb9e55170227a9a9a628725f0deba082 Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Thu, 5 Sep 2013 17:38:25 +0100 Subject: [PATCH 2/8] added manual association --- Datatables/Datatable.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Datatables/Datatable.php b/Datatables/Datatable.php index 80fd8d0..6798adc 100755 --- a/Datatables/Datatable.php +++ b/Datatables/Datatable.php @@ -512,6 +512,18 @@ 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) { + $newAssociation = array('containsCollections' => false); + $fields = explode('.', $name); + $this->setRelatedEntityColumnInfo($newAssociation, $fields); + $this->associations[] = $newAssociation; + } /** * Configure joins for entity associations From feca7e54bf3b61656b9c5379fcb3607ba75eb734 Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Sat, 7 Sep 2013 11:36:44 +0100 Subject: [PATCH 3/8] Added support for multiple entity managers --- Datatables/DatatableManager.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Datatables/DatatableManager.php b/Datatables/DatatableManager.php index b7edf4f..ed1d340 100755 --- a/Datatables/DatatableManager.php +++ b/Datatables/DatatableManager.php @@ -21,6 +21,11 @@ class DatatableManager * @var boolean Whether or not to use the Doctrine Paginator utility by default */ protected $useDoctrinePaginator; + + /** + * the entity manager + **/ + protected $manager; public function __construct(DoctrineRegistry $doctrine, ContainerInterface $container, $useDoctrinePaginator) { @@ -48,18 +53,19 @@ protected function getClassName($className) { * @param string An entity class name or alias * @return object Get a DataTable instance for the given entity */ - public function getDatatable($class) + public function getDatatable($class, $manager = "default") { + $this->manager = $manager; $class = $this->getClassName($class); - $metadata = $this->doctrine->getManager()->getClassMetadata($class); - $repository = $this->doctrine->getRepository($class); + $metadata = $this->doctrine->getManager($this->manager)->getClassMetadata($class); + $repository = $this->doctrine->getManager($this->manager)->getRepository($class); $datatable = new Datatable( $this->container->get('request')->query->all(), $this->doctrine->getRepository($class), - $this->doctrine->getManager()->getClassMetadata($class), - $this->doctrine->getManager(), + $this->doctrine->getManager($this->manager)->getClassMetadata($class), + $this->doctrine->getManager($this->manager), $this->container->get('lankit_datatables.serializer') ); return $datatable->useDoctrinePaginator($this->useDoctrinePaginator); From e028f24a129f8732507139062f46415452e82a49 Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Sat, 7 Sep 2013 11:51:14 +0100 Subject: [PATCH 4/8] Fixed problem with multiple entity managers support - working now --- Datatables/DatatableManager.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Datatables/DatatableManager.php b/Datatables/DatatableManager.php index ed1d340..1b7ccbb 100755 --- a/Datatables/DatatableManager.php +++ b/Datatables/DatatableManager.php @@ -22,10 +22,10 @@ class DatatableManager */ protected $useDoctrinePaginator; - /** - * the entity manager - **/ - protected $manager; + /** + * the entity manager + **/ + protected $manager; public function __construct(DoctrineRegistry $doctrine, ContainerInterface $container, $useDoctrinePaginator) { @@ -43,7 +43,7 @@ public function __construct(DoctrineRegistry $doctrine, ContainerInterface $cont protected function getClassName($className) { if (strpos($className, ':') !== false) { list($namespaceAlias, $simpleClassName) = explode(':', $className); - $className = $this->doctrine->getManager()->getConfiguration() + $className = $this->doctrine->getManager($this->manager)->getConfiguration() ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; } return $className; @@ -63,7 +63,7 @@ public function getDatatable($class, $manager = "default") $datatable = new Datatable( $this->container->get('request')->query->all(), - $this->doctrine->getRepository($class), + $this->doctrine->getManager($this->manager)->getRepository($class), $this->doctrine->getManager($this->manager)->getClassMetadata($class), $this->doctrine->getManager($this->manager), $this->container->get('lankit_datatables.serializer') From de409fa79e7b9afcd1144fac369470407a6cf6cb Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Thu, 12 Sep 2013 13:49:25 +0100 Subject: [PATCH 5/8] Updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 380e437..259f07e 100755 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ composer.lock .*.sw[a-z] *.un~ Session.vim +/nbproject/private/ +nbproject/project.properties +nbproject/project.xml From 67c0746b24693a4f598efce90637ec9dfcb9aeb8 Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Mon, 23 Dec 2013 15:47:51 +0000 Subject: [PATCH 6/8] when search box isnt used then the sSearch field isnt part of the request - now checks if sSearch is set and deals with accordingly --- Datatables/Datatable.php | 91 +++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 47 deletions(-) mode change 100755 => 100644 Datatables/Datatable.php diff --git a/Datatables/Datatable.php b/Datatables/Datatable.php old mode 100755 new mode 100644 index 6798adc..c218ee8 --- a/Datatables/Datatable.php +++ b/Datatables/Datatable.php @@ -1,4 +1,5 @@ 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']; @@ -271,7 +272,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); @@ -293,7 +294,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) + { $mdataName = implode('.', $fields); $lastField = Container::camelize(array_pop($fields)); $joinName = $this->tableName; @@ -311,12 +313,10 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields $association['containsCollections'] = true; } $metadata = $this->em->getClassMetadata( - $metadata->getAssociationTargetClass($entityName) + $metadata->getAssociationTargetClass($entityName) ); $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 (!isset($this->assignedJoins[$joinName])) { @@ -324,11 +324,9 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields $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' ); } } @@ -336,8 +334,7 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields // 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; @@ -352,13 +349,13 @@ 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' ); } @@ -380,7 +377,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; @@ -457,10 +454,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] ); } } @@ -477,12 +473,11 @@ 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'] . "%"); } @@ -492,14 +487,13 @@ public function setWhere(QueryBuilder $qb) // 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) { @@ -512,13 +506,14 @@ 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) { + public function addManualAssociation($name) + { $newAssociation = array('containsCollections' => false); $fields = explode('.', $name); $this->setRelatedEntityColumnInfo($newAssociation, $fields); @@ -534,7 +529,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 @@ -588,7 +583,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); @@ -606,8 +601,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')); } /** @@ -619,7 +615,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)) { @@ -629,7 +625,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; @@ -702,8 +698,7 @@ public function getSearchResults($resultType = '') { if (empty($resultType) || !defined('self::RESULT_' . strtoupper($resultType))) { $resultType = $this->defaultResultType; - } - else { + } else { $resultType = constant('self::RESULT_' . strtoupper($resultType)); } @@ -748,10 +743,10 @@ 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); } @@ -759,7 +754,7 @@ public function getCountAllResults() return (int) $qb->getQuery()->getSingleScalarResult(); } - + /** * @return int Total query results after searches/filtering */ @@ -775,7 +770,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."); } @@ -801,11 +797,12 @@ public function getAmount() public function getSearch() { - return "%" . $this->search . "%"; + return "%" . $this->search . "%"; } public function getQueryBuilder() { - return $this->qb; + return $this->qb; } + } From 36d59c453eb722c516ea1ff5199b0074159df0f3 Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Thu, 6 Feb 2014 14:15:58 +0000 Subject: [PATCH 7/8] Removed unused code in constructor --- Datatables/DatatableManager.php | 145 ++++++++++++++++---------------- 1 file changed, 71 insertions(+), 74 deletions(-) mode change 100755 => 100644 Datatables/DatatableManager.php diff --git a/Datatables/DatatableManager.php b/Datatables/DatatableManager.php old mode 100755 new mode 100644 index 1b7ccbb..2dd9837 --- a/Datatables/DatatableManager.php +++ b/Datatables/DatatableManager.php @@ -1,74 +1,71 @@ -doctrine = $doctrine; - $this->container = $container; - $this->useDoctrinePaginator = $useDoctrinePaginator; - } - - /** - * Given an entity class name or possible alias, convert it to the full class name - * - * @param string The entity class name or alias - * @return string The entity class name - */ - protected function getClassName($className) { - if (strpos($className, ':') !== false) { - list($namespaceAlias, $simpleClassName) = explode(':', $className); - $className = $this->doctrine->getManager($this->manager)->getConfiguration() - ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; - } - return $className; - } - - /** - * @param string An entity class name or alias - * @return object Get a DataTable instance for the given entity - */ - public function getDatatable($class, $manager = "default") - { - $this->manager = $manager; - $class = $this->getClassName($class); - - $metadata = $this->doctrine->getManager($this->manager)->getClassMetadata($class); - $repository = $this->doctrine->getManager($this->manager)->getRepository($class); - - $datatable = new Datatable( - $this->container->get('request')->query->all(), - $this->doctrine->getManager($this->manager)->getRepository($class), - $this->doctrine->getManager($this->manager)->getClassMetadata($class), - $this->doctrine->getManager($this->manager), - $this->container->get('lankit_datatables.serializer') - ); - return $datatable->useDoctrinePaginator($this->useDoctrinePaginator); - } -} - +doctrine = $doctrine; + $this->container = $container; + $this->useDoctrinePaginator = $useDoctrinePaginator; + } + + /** + * Given an entity class name or possible alias, convert it to the full class name + * + * @param string The entity class name or alias + * @return string The entity class name + */ + protected function getClassName($className) { + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + $className = $this->doctrine->getManager($this->manager)->getConfiguration() + ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + return $className; + } + + /** + * @param string An entity class name or alias + * @return object Get a DataTable instance for the given entity + */ + public function getDatatable($class, $manager = "default") + { + $this->manager = $manager; + $class = $this->getClassName($class); + + $datatable = new Datatable( + $this->container->get('request')->query->all(), + $this->doctrine->getManager($this->manager)->getRepository($class), + $this->doctrine->getManager($this->manager)->getClassMetadata($class), + $this->doctrine->getManager($this->manager), + $this->container->get('lankit_datatables.serializer') + ); + return $datatable->useDoctrinePaginator($this->useDoctrinePaginator); + } +} + From 8b9fb0c27368a56f81c692f6be47dd489f13a60d Mon Sep 17 00:00:00 2001 From: Paul Mansell Date: Wed, 12 Nov 2014 12:36:00 +0000 Subject: [PATCH 8/8] Added ability to join same table multiple times --- Datatables/Datatable.php | 61 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/Datatables/Datatable.php b/Datatables/Datatable.php index c218ee8..944ed02 100644 --- a/Datatables/Datatable.php +++ b/Datatables/Datatable.php @@ -194,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) { @@ -294,7 +299,7 @@ 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)); @@ -315,10 +320,19 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields $metadata = $this->em->getClassMetadata( $metadata->getAssociationTargetClass($entityName) ); + + if ($metadata->hasField(lcfirst($lastField))) { + $association['type'] = $metadata->getTypeOfField(lcfirst($lastField)); + } + $joinName .= '_' . $this->getJoinName( $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; @@ -362,6 +376,41 @@ protected function setSingleFieldColumnInfo(array &$association, $fieldName) $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; } /** @@ -479,7 +528,11 @@ public function setWhere(QueryBuilder $qb) $orExpr->add($qb->expr()->like( $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); @@ -512,11 +565,11 @@ public function setWhere(QueryBuilder $qb) * * @param type $name - the dotted notation like in mData of the field you need adding */ - public function addManualAssociation($name) + public function addManualAssociation($name, $multipleJoin = false) { $newAssociation = array('containsCollections' => false); $fields = explode('.', $name); - $this->setRelatedEntityColumnInfo($newAssociation, $fields); + $this->setRelatedEntityColumnInfo($newAssociation, $fields, $multipleJoin); $this->associations[] = $newAssociation; }