View source
<?php
namespace Drupal\nodeorder;
use Drupal\Core\Cache\Cache;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
class NodeOrderManager implements NodeOrderManagerInterface {
protected $configFactory;
protected $termStorage;
protected $cache;
public function __construct(ConfigFactoryInterface $config_factory, EntityManagerInterface $entity_manager, CacheBackendInterface $cache) {
$this->configFactory = $config_factory;
$this->termStorage = $entity_manager
->getStorage('taxonomy_term');
$this->cache = $cache;
}
public function addToList(NodeInterface $node, $tid) {
$weights = $this
->getTermMinMax($tid);
\Drupal::database()
->update('taxonomy_index')
->fields([
'weight' => $weights['min'] - 1,
])
->condition('nid', $node
->id())
->condition('tid', $tid)
->execute();
$taxonomy_nids = \Drupal::database()
->select('taxonomy_index', 'ti')
->fields('ti', [
'nid',
])
->condition('ti.tid', $tid)
->orderBy('ti.weight')
->execute()
->fetchCol('nid');
$new_node_out_of_range = count($taxonomy_nids) % 2 == 0 && $weights['min'] == -ceil(count($taxonomy_nids) / 2);
if ($new_node_out_of_range) {
$top_range_nids = [];
$previous_weight = $weights['min'] - 2;
foreach ($taxonomy_nids as $taxonomy_nid) {
$taxonomy_node_weight = \Drupal::database()
->select('taxonomy_index', 'i')
->fields('i', [
'weight',
])
->condition('tid', $tid)
->condition('nid', $taxonomy_nid)
->execute()
->fetchField();
if ($taxonomy_node_weight > $previous_weight + 1) {
break;
}
$previous_weight = $taxonomy_node_weight;
$top_range_nids[] = $taxonomy_nid;
}
$query = \Drupal::database()
->update('taxonomy_index');
$query
->expression('weight', 'weight + 1');
$query
->condition('nid', $top_range_nids, 'IN')
->condition('tid', $tid)
->execute();
}
$this
->getTermMinMax($tid, TRUE);
}
public function getTermMinMax($tid, $reset = FALSE) {
static $min_weights = [];
static $max_weights = [];
if ($reset) {
$min_weights = [];
$max_weights = [];
}
if (!isset($min_weights[$tid]) || !isset($max_weights[$tid])) {
$query = \Drupal::database()
->select('taxonomy_index', 'i')
->fields('i', [
'tid',
])
->condition('tid', $tid)
->groupBy('tid');
$query
->addExpression('MAX(weight)', 'max_weight');
$query
->addExpression('MIN(weight)', 'min_weight');
$record = $query
->execute()
->fetch();
$min_weights[$tid] = $record->min_weight;
$max_weights[$tid] = $record->max_weight;
}
$weights['min'] = $min_weights[$tid];
$weights['max'] = $max_weights[$tid];
return $weights;
}
public function vocabularyIsOrderable($vid) {
$vocabularies = $this->configFactory
->get('nodeorder.settings')
->get('vocabularies');
return !empty($vocabularies[$vid]);
}
public function selectNodes($tids = [], $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC', $count = -1) {
if (count($tids) > 0) {
$descendant_tids = [];
if ($depth === 'all') {
$depth = NULL;
}
foreach ($tids as $index => $tid) {
$term = $this->termStorage
->load($tid);
$tree = $this->termStorage
->loadTree($term
->getVocabularyId(), $tid, $depth);
$descendant_tids[] = array_merge([
$tid,
], array_map(function ($value) {
return $value
->id();
}, $tree));
}
if ($operator == 'or') {
$args = call_user_func_array('array_merge', $descendant_tids);
$placeholders = db_placeholders($args, 'int');
$sql = 'SELECT DISTINCT(n.nid), nd.sticky, nd.title, nd.created, tn.weight FROM {node} n LEFT JOIN {node_field_data} nd INNER JOIN {taxonomy_index} tn ON n.vid = tn.vid WHERE tn.tid IN (' . $placeholders . ') AND n.status = 1 ORDER BY ' . $order;
$sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {taxonomy_index} tn ON n.vid = tn.vid WHERE tn.tid IN (' . $placeholders . ') AND n.status = 1';
}
else {
$args = [];
$query = \Drupal::database()
->select('node', 'n');
$query
->join('node_field_data', 'nd', 'nd.nid = n.nid');
$query
->condition('nd.status', 1);
foreach ($descendant_tids as $index => $tids) {
$query
->join('taxonomy_index', "tn{$index}", "n.nid = tn{$index}.nid");
$query
->condition("tn{$index}.tid", $tids, 'IN');
}
$query
->fields('nd', [
'nid',
'sticky',
'title',
'created',
]);
$query
->fields('tn0', [
'weight',
]);
}
if ($pager) {
if ($count == -1) {
$count = $this->configFactory
->get('nodeorder.settings')
->get('default_nodes_main');
}
$result = pager_query($sql, $count, 0, $sql_count, $args);
}
else {
if ($count == -1) {
$count = $this->configFactory
->get('nodeorder.settings')
->get('feed_default_items');
}
if ($count == 0) {
$result = $query
->execute();
}
else {
$result = db_query_range($sql, $args);
}
}
}
return $result;
}
public function canBeOrdered(NodeInterface $node) {
$cid = 'nodeorder:can_be_ordered:' . $node
->getType();
if (($cache = $this->cache
->get($cid)) && !empty($cache->data)) {
return $cache->data;
}
else {
$can_be_ordered = FALSE;
$nodeorder_vocabularies = [];
foreach ($node
->getFieldDefinitions() as $field) {
if ($field
->getType() != 'entity_reference' || $field
->getSetting('target_type') != 'taxonomy_term') {
continue;
}
foreach ($field
->getSetting('handler_settings')['target_bundles'] as $vocabulary) {
$nodeorder_vocabularies[] = $vocabulary;
}
}
foreach ($nodeorder_vocabularies as $vid) {
if (Vocabulary::load($vid)) {
$can_be_ordered = TRUE;
}
}
$this->cache
->set($cid, $can_be_ordered, Cache::PERMANENT, [
'nodeorder',
]);
return $can_be_ordered;
}
}
public function getOrderableTids(NodeInterface $node, $reset = FALSE) {
$cid = 'nodeorder:orderable_tids:' . $node
->getType();
if (!$reset && ($cache = $this->cache
->get($cid)) && !empty($cache->data)) {
$tids = $cache->data;
}
else {
$vocabularies = [];
foreach ($this->configFactory
->get('nodeorder.settings')
->get('vocabularies') as $vid => $orderable) {
if ($orderable) {
$vocabularies[] = $vid;
}
}
if (!empty($vocabularies)) {
$query = \Drupal::database()
->select('taxonomy_index', 'i');
$query
->join('taxonomy_term_data', 'd', 'd.tid = i.tid');
$query
->condition('i.nid', $node
->id())
->condition('d.vid', $vocabularies, 'IN')
->fields('i', [
'tid',
]);
$tids = $query
->execute()
->fetchCol('tid');
}
else {
$tids = [];
}
$this->cache
->set($cid, $tids, Cache::PERMANENT, [
'nodeorder',
]);
}
return $tids;
}
public function getOrderableTidsFromNode(NodeInterface $node) {
$tids = [];
foreach ($node
->getFieldDefinitions() as $field) {
if ($field
->getType() == 'entity_reference' && $field
->getSetting('target_type') == 'taxonomy_term') {
$field_name = $field
->getName();
foreach ($node
->getTranslationLanguages() as $langcode) {
$translated = $node
->getTranslation($langcode
->getId());
foreach ($translated->{$field_name} as $item) {
$term = $item
->getValue();
if (!empty($term['target_id'])) {
$tids[$term['target_id']] = $term['target_id'];
}
}
}
}
}
return $tids;
}
public function handleListsDecrease($tid) {
$taxonomy_nids = \Drupal::database()
->select('taxonomy_index', 'ti')
->fields('ti', [
'nid',
])
->condition('ti.tid', $tid)
->orderBy('ti.weight')
->execute()
->fetchCol('nid');
if (!count($taxonomy_nids)) {
return;
}
$weights = $this
->getTermMinMax($tid, TRUE);
$range_border = ceil(count($taxonomy_nids) / 2);
$border_out_of_range = $weights['min'] < -$range_border || $weights['max'] > $range_border;
if ($border_out_of_range) {
$weight = -$range_border;
foreach ($taxonomy_nids as $nid) {
\Drupal::database()
->update('taxonomy_index')
->fields([
'weight' => $weight,
])
->condition('nid', $nid)
->condition('tid', $tid)
->execute();
$weight++;
}
$this
->getTermMinMax($tid, TRUE);
}
}
}