Skip to content

Commit

Permalink
Merge branch 'master' of github.com:heimrichhannot/contao-utils-bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Wagner committed Feb 14, 2018
2 parents d565e09 + 84831cc commit 558ac00
Show file tree
Hide file tree
Showing 9 changed files with 572 additions and 2 deletions.
56 changes: 56 additions & 0 deletions src/Choice/ModelInstanceChoice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/*
* Copyright (c) 2018 Heimrich & Hannot GmbH
*
* @license LGPL-3.0-or-later
*/

namespace HeimrichHannot\UtilsBundle\Choice;

use Contao\System;

class ModelInstanceChoice extends AbstractChoice
{
/**
* @return array
*/
protected function collect()
{
$context = $this->getContext();
$choices = [];

$instances = System::getContainer()->get('huh.utils.model')->findModelInstancesBy(
$context['dataContainer'],
$context['columns'] ?: null,
$context['values'] ?: null,
is_array($context['options']) ? $context['options'] : []
);

if (null === $instances) {
return $choices;
}

while ($instances->next()) {
$label = $instances->id;

if ($context['labelPattern']) {
$label = preg_replace_callback(
'@%([^%]+)%@i',
function ($matches) use ($instances) {
return $instances->{$matches[1]};
},
$context['labelPattern']
);
}

$choices[$instances->id] = $label;
}

if (!$context['skipSorting']) {
asort($choices);
}

return $choices;
}
}
117 changes: 116 additions & 1 deletion src/Database/DatabaseUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Contao\Database;
use Contao\Model;
use Contao\System;
use Doctrine\DBAL\Query\QueryBuilder;

class DatabaseUtil
{
Expand All @@ -29,6 +30,9 @@ class DatabaseUtil
const OPERATOR_GREATER_EQUAL = 'greaterequal';
const OPERATOR_IN = 'in';
const OPERATOR_NOT_IN = 'notin';
const OPERATOR_IS_NULL = 'isnull';
const OPERATOR_IS_NOT_NULL = 'isnotnull';
const OPERATOR_REGEXP = 'regexp';

const ON_DUPLICATE_KEY_IGNORE = 'IGNORE';
const ON_DUPLICATE_KEY_UPDATE = 'UPDATE';
Expand All @@ -44,6 +48,27 @@ class DatabaseUtil
self::OPERATOR_GREATER_EQUAL,
self::OPERATOR_IN,
self::OPERATOR_NOT_IN,
self::OPERATOR_IS_NULL,
self::OPERATOR_IS_NOT_NULL,
self::OPERATOR_REGEXP,
];

/**
* Maps operators of this class to its corresponding Doctrine ExpressionBuilder method.
*/
const OPERATOR_MAPPING = [
self::OPERATOR_LIKE => 'like',
self::OPERATOR_UNLIKE => 'notLike',
self::OPERATOR_EQUAL => 'eq',
self::OPERATOR_UNEQUAL => 'neq',
self::OPERATOR_LOWER => 'lt',
self::OPERATOR_LOWER_EQUAL => 'lte',
self::OPERATOR_GREATER => 'gt',
self::OPERATOR_GREATER_EQUAL => 'gte',
self::OPERATOR_IN => 'in',
self::OPERATOR_NOT_IN => 'notIn',
self::OPERATOR_IS_NULL => 'isNull',
self::OPERATOR_IS_NOT_NULL => 'isNotNull',
];

/** @var ContaoFrameworkInterface */
Expand Down Expand Up @@ -165,7 +190,9 @@ function ($val) {

$i = 0;

$columnWildcards = array_map(function ($val) { return '?'; }, $fields);
$columnWildcards = array_map(function ($val) {
return '?';
}, $fields);

foreach ($data as $key => $varData) {
if (0 == $i) {
Expand Down Expand Up @@ -335,6 +362,10 @@ public function transformVerboseOperator(string $verboseOperator)
break;
case static::OPERATOR_NOT_IN:
return 'NOT IN';
case static::OPERATOR_IS_NULL:
return 'NOT IN';
case static::OPERATOR_IS_NOT_NULL:
return 'IS NOT NULL';
break;
}

Expand Down Expand Up @@ -416,6 +447,12 @@ function ($value) {
)
).')';
break;
case static::OPERATOR_IS_NULL:
$pattern = '';
break;
case static::OPERATOR_IS_NOT_NULL:
$pattern = '';
break;
default:
$values[] = '%'.($addQuotes ? '"'.$value.'"' : $value).'%';
break;
Expand All @@ -425,4 +462,82 @@ function ($value) {

return ["$field $operator $pattern", $values];
}

public function composeWhereForQueryBuilder(QueryBuilder $queryBuilder, string $field, string $operator, array $dca, $value = null)
{
$wildcard = ':'.$field;
$where = '';

switch ($operator) {
case self::OPERATOR_LIKE:
$where = $queryBuilder->expr()->like($field, $wildcard);
$queryBuilder->setParameter($wildcard, '%'.$value.'%');
break;
case self::OPERATOR_UNLIKE:
$where = $queryBuilder->expr()->notLike($field, $wildcard);
$queryBuilder->setParameter($wildcard, '%'.$value.'%');
break;
case self::OPERATOR_EQUAL:
$where = $queryBuilder->expr()->eq($field, $wildcard);
$queryBuilder->setParameter($wildcard, $value);
break;
case self::OPERATOR_UNEQUAL:
$where = $queryBuilder->expr()->neq($field, $wildcard);
$queryBuilder->setParameter($wildcard, $value);
break;
case self::OPERATOR_LOWER:
$where = $queryBuilder->expr()->lt($field, $wildcard);
$queryBuilder->setParameter($wildcard, $value);
break;
case self::OPERATOR_LOWER_EQUAL:
$where = $queryBuilder->expr()->lte($field, $wildcard);
$queryBuilder->setParameter($wildcard, $value);
break;
case self::OPERATOR_GREATER:
$where = $queryBuilder->expr()->gt($field, $wildcard);
$queryBuilder->setParameter($wildcard, $value);
break;
case self::OPERATOR_GREATER_EQUAL:
$where = $queryBuilder->expr()->gte($field, $wildcard);
$queryBuilder->setParameter($wildcard, $value);
break;
case self::OPERATOR_IN:
$where = $queryBuilder->expr()->in($field, $wildcard);
// always handle array items as strings
$queryBuilder->setParameter($wildcard, $value, \PDO::PARAM_STR);
break;
case self::OPERATOR_NOT_IN:
$where = $queryBuilder->expr()->in($field, $wildcard);
// always handle array items as strings
$queryBuilder->setParameter($wildcard, $value, \PDO::PARAM_STR);
break;
case self::OPERATOR_IS_NULL:
$where = $queryBuilder->expr()->isNull($field);
break;
case self::OPERATOR_IS_NOT_NULL:
$where = $queryBuilder->expr()->isNotNull($field);
break;
case self::OPERATOR_REGEXP:
$where = $field.' REGEXP '.$wildcard;

if (isset($dca['eval']['multiple']) && $dca['eval']['multiple']) {
// match a serialized blob
if (is_array($value)) {
// build a regexp alternative, e.g. (:"1";|:"2";)
$queryBuilder->setParameter($wildcard, '('.implode('|', array_map(function ($val) {
return ':"'.$val.'";';
}, $value)).')');
} else {
$queryBuilder->setParameter($wildcard, ':"'.$value.'";');
}
} else {
// TODO: this makes no sense, yet
$queryBuilder->setParameter($wildcard, $value);
}

break;
}

return $where;
}
}
117 changes: 117 additions & 0 deletions src/Dca/DcaUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,26 @@

namespace HeimrichHannot\UtilsBundle\Dca;

use Contao\BackendUser;
use Contao\Controller;
use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
use Contao\Database;
use Contao\Database\Result;
use Contao\DataContainer;
use Contao\FrontendUser;
use Contao\StringUtil;
use Contao\System;

class DcaUtil
{
const PROPERTY_SESSION_ID = 'sessionID';
const PROPERTY_AUTHOR = 'author';
const PROPERTY_AUTHOR_TYPE = 'authorType';

const AUTHOR_TYPE_NONE = 'none';
const AUTHOR_TYPE_MEMBER = 'member';
const AUTHOR_TYPE_USER = 'user';

/** @var ContaoFrameworkInterface */
protected $framework;

Expand Down Expand Up @@ -352,4 +362,111 @@ public function generateAlias(string $alias, int $id, string $table, string $tit

return $alias;
}

public function addAuthorFieldAndCallback(string $table)
{
Controller::loadDataContainer($table);

// callbacks
$GLOBALS['TL_DCA'][$table]['config']['oncreate_callback']['setAuthorIDOnCreate'] = ['huh.utils.dca', 'setAuthorIDOnCreate'];
$GLOBALS['TL_DCA'][$table]['config']['onload_callback']['modifyAuthorPaletteOnLoad'] = ['huh.utils.dca', 'modifyAuthorPaletteOnLoad', true];

// fields
$GLOBALS['TL_DCA'][$table]['fields'][static::PROPERTY_AUTHOR_TYPE] = [
'label' => &$GLOBALS['TL_LANG']['MSC']['utilsBundle']['authorType'],
'exclude' => true,
'filter' => true,
'default' => static::AUTHOR_TYPE_NONE,
'inputType' => 'select',
'options' => [
static::AUTHOR_TYPE_NONE,
static::AUTHOR_TYPE_MEMBER,
static::AUTHOR_TYPE_USER,
],
'reference' => $GLOBALS['TL_LANG']['MSC']['utilsBundle']['authorType'],
'eval' => ['doNotCopy' => true, 'submitOnChange' => true, 'mandatory' => true, 'tl_class' => 'w50 clr'],
'sql' => "varchar(255) NOT NULL default 'none'",
];

$GLOBALS['TL_DCA'][$table]['fields'][static::PROPERTY_AUTHOR] = [
'label' => &$GLOBALS['TL_LANG']['MSC']['utilsBundle']['author'],
'exclude' => true,
'search' => true,
'filter' => true,
'inputType' => 'select',
'options_callback' => function () {
return \Contao\System::getContainer()->get('huh.utils.choice.model_instance')->getCachedChoices(
[
'dataContainer' => 'tl_member',
'labelPattern' => '%firstname% %lastname% (ID %id%)',
]
);
},
'eval' => [
'doNotCopy' => true,
'chosen' => true,
'includeBlankOption' => true,
'tl_class' => 'w50',
],
'sql' => "int(10) unsigned NOT NULL default '0'",
];
}

public function setAuthorIDOnCreate(string $table, int $id, array $row, DataContainer $dc)
{
$model = System::getContainer()->get('huh.utils.model')->findModelInstanceByPk($table, $id);
$db = Database::getInstance();

if (null === $model
|| !$db->fieldExists(static::PROPERTY_AUTHOR_TYPE, $table)
|| !$db->fieldExists(static::PROPERTY_AUTHOR, $table)
) {
return false;
}

if (System::getContainer()->get('huh.utils.container')->isFrontend()) {
if (FE_USER_LOGGED_IN) {
$model->{static::PROPERTY_AUTHOR_TYPE} = static::AUTHOR_TYPE_MEMBER;
$model->{static::PROPERTY_AUTHOR} = FrontendUser::getInstance()->id;
$model->save();
}
} else {
$model->{static::PROPERTY_AUTHOR_TYPE} = static::AUTHOR_TYPE_USER;
$model->{static::PROPERTY_AUTHOR} = BackendUser::getInstance()->id;
$model->save();
}
}

public function modifyAuthorPaletteOnLoad(DataContainer $dc)
{
if (!System::getContainer()->get('huh.utils.container')->isBackend()) {
return false;
}

if (null === $dc || !$dc->id) {
return false;
}

if (null === ($model = System::getContainer()->get('huh.utils.model')->findModelInstanceByPk($dc->table, $dc->id))) {
return false;
}

$dca = &$GLOBALS['TL_DCA'][$dc->table];

// author handling
if ($model->{static::PROPERTY_AUTHOR_TYPE} == static::AUTHOR_TYPE_NONE) {
unset($dca['fields']['author']);
}

if ($model->{static::PROPERTY_AUTHOR_TYPE} == static::AUTHOR_TYPE_USER) {
$dca['fields']['author']['options_callback'] = function () {
return \Contao\System::getContainer()->get('huh.utils.choice.model_instance')->getCachedChoices(
[
'dataContainer' => 'tl_user',
'labelPattern' => '%name% (ID %id%)',
]
);
};
}
}
}
2 changes: 1 addition & 1 deletion src/Model/ModelUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function findModelInstanceByPk(string $table, $pk, array $options = [])
*
* @return mixed
*/
public function findModelInstancesBy(string $table, array $columns, array $values, array $options = [])
public function findModelInstancesBy(string $table, array $columns = null, array $values = null, array $options = [])
{
/*
* @var Model
Expand Down
5 changes: 5 additions & 0 deletions src/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ services:
public: true
arguments:
- "@contao.framework"
huh.utils.choice.model_instance:
class: HeimrichHannot\UtilsBundle\Choice\ModelInstanceChoice
public: true
arguments:
- "@contao.framework"
huh.utils.routing:
class: HeimrichHannot\UtilsBundle\Request\RoutingUtil
public: true
Expand Down
Loading

0 comments on commit 558ac00

Please sign in to comment.