gestione categorie interne & fix

This commit is contained in:
2025-09-01 12:56:30 +02:00
parent 16ac92a59e
commit f9b01eb01e
40 changed files with 2252 additions and 424 deletions

View File

@ -3,20 +3,29 @@ namespace Pcrt\Component\Circolari\Site\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
class DisplayController extends BaseController
{
protected $default_view = 'circolare'; // default se manca ?view=
public function display($cachable = false, $urlparams = [])
{
// Se qualcuno forza un view strano, non rompiamo
$view = $this->input->getCmd('view', $this->default_view);
if (!in_array($view, ['circolare'], true)) {
$view = $this->default_view;
$this->input->set('view', $view);
$app = Factory::getApplication();
$in = $app->input;
$view = $in->getCmd('view', '');
$id = $in->getInt('id', 0);
if ($id > 0) {
// Singolo solo se c'è l'id
$in->set('view', 'circolare');
} else {
// Nessun id: forziamo la lista (anche se il menu chiede "circolare")
if ($view === '' || $view === 'circolare' || $view === 'category') {
$in->set('view', 'circolari'); // <- lista
}
}
return parent::display($cachable, $urlparams);
}
}

View File

@ -3,31 +3,75 @@ namespace Pcrt\Component\Circolari\Site\Model;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\ItemModel;
use Joomla\CMS\Factory;
class CircolareModel extends ItemModel
{
protected function populateState(): void
protected function populateState()
{
$app = Factory::getApplication();
$this->setState('circolare.id', (int) $app->input->getInt('id'));
$id = $app->input->getInt('id', 0);
$this->setState('circolare.id', $id);
$categoria_id = $app->input->getInt('categoria_id', 0);
$this->setState('filter.categoria_id', $categoria_id);
parent::populateState();
}
public function getItem($pk = null)
{
$pk = $pk ?: (int) $this->getState('circolare.id');
if (!$pk) return null;
$pk = (int) ($pk ?: $this->getState('circolare.id'));
$db = Factory::getContainer()->get('DatabaseDriver');
if ($pk <= 0) {
return null;
}
$db = $this->getDatabase();
$q = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__circolari'))
->where($db->quoteName('id') . ' = ' . (int) $pk)
->where($db->quoteName('state') . ' = 1');
$db->setQuery($q);
->select('c.*') // <<< evita errori di colonne
->from($db->quoteName('#__circolari') . ' AS c')
->where('c.id = ' . (int) $pk);
return $db->loadObject();
// Se esistono le colonne, applica filtri
$cols = array_change_key_case(
$db->getTableColumns($db->replacePrefix('#__circolari'), false),
CASE_LOWER
);
// categoria_id (se impostato e colonna esiste)
$categoria_id = (int) $this->getState('filter.categoria_id', 0);
if ($categoria_id > 0 && isset($cols['categoria_id'])) {
$q->where('c.categoria_id = ' . $categoria_id);
}
// stato (se colonna esiste)
if (isset($cols['state'])) {
$q->where('COALESCE(c.state, 1) = 1');
}
$db->setQuery($q);
$row = $db->loadObject();
return $row ?: null;
}
}
/**
* Incrementa gli hits della circolare.
*/
public function hit($pk = null)
{
$pk = $pk ?: (int) $this->getState($this->getName() . '.id');
if (!$pk) {
return false;
}
$db = $this->getDatabase();
$query = $db->getQuery(true)
->update($db->quoteName('#__circolari'))
->set($db->quoteName('hits') . ' = ' . $db->quoteName('hits') . ' + 1')
->where($db->quoteName('id') . ' = ' . (int)$pk);
$db->setQuery($query)->execute();
return true;
}

View File

@ -0,0 +1,119 @@
<?php
namespace Pcrt\Component\Circolari\Site\Model;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\ListModel;
class CircolariModel extends ListModel
{
protected $context = 'com_circolari.circolari';
// campi ammessi per sort
protected $filter_fields = [
'id', 'c.id',
'title', 'c.title',
'created', 'c.created',
'hits', 'c.hits',
'categoria_id', 'c.categoria_id',
'state', 'c.state',
];
protected function populateState($ordering = 'c.created', $direction = 'DESC')
{
$app = Factory::getApplication();
// paging
$limit = $app->getUserStateFromRequest($this->context . '.list.limit', 'limit', $app->get('list_limit'), 'uint');
$this->setState('list.limit', $limit);
$start = $app->getUserStateFromRequest($this->context . '.list.start', 'limitstart', 0, 'uint');
$this->setState('list.start', $start);
// sorting
$orderCol = $app->getUserStateFromRequest($this->context . '.list.ordering', 'filter_order', $ordering, 'cmd');
if (!\in_array($orderCol, $this->filter_fields, true)) {
$orderCol = $ordering;
}
$this->setState('list.ordering', $orderCol);
$orderDirn = strtoupper($app->getUserStateFromRequest($this->context . '.list.direction', 'filter_order_Dir', $direction, 'cmd'));
$this->setState('list.direction', $orderDirn === 'ASC' ? 'ASC' : 'DESC');
// filtro testuale
$search = $app->getUserStateFromRequest($this->context . '.list.filter', 'filter-search', '', 'string');
$this->setState('list.filter', $search);
// categoria (se usi categoria_id)
$this->setState('filter.categoria_id', $app->input->getInt('categoria_id', 0));
parent::populateState($ordering, $direction);
}
protected function getListQuery()
{
$db = $this->getDatabase();
$q = $db->getQuery(true)
->select('c.*')
->from($db->quoteName('#__circolari', 'c'));
// colonne effettive in tabella
$cols = array_change_key_case($db->getTableColumns($db->replacePrefix('#__circolari')));
// pubblicati
if (isset($cols['state'])) {
$q->where('COALESCE(c.state,1)=1');
}
// categoria_id
$categoria_id = (int) $this->getState('filter.categoria_id', 0);
if ($categoria_id > 0 && isset($cols['categoria_id'])) {
$q->where('c.categoria_id=' . $categoria_id);
}
// search su title
$search = trim((string) $this->getState('list.filter', ''));
if ($search !== '' && isset($cols['title'])) {
$like = $db->quote('%' . $db->escape($search, true) . '%', false);
$q->where('c.title LIKE ' . $like);
}
// ordinamento
$orderCol = $this->getState('list.ordering', 'c.created');
$orderDir = $this->getState('list.direction', 'DESC');
$q->order($db->escape($orderCol . ' ' . $orderDir));
return $q;
}
// conteggio robusto (gestisce DISTINCT/GROUP BY)
public function getTotal()
{
if (isset($this->total)) {
return $this->total;
}
$db = $this->getDatabase();
$sub = $this->getListQuery();
$query = $db->getQuery(true)
->select('COUNT(*)')
->from('(' . $sub . ') AS x');
$db->setQuery($query);
$this->total = (int) $db->loadResult();
return $this->total;
}
public function getItems()
{
$items = parent::getItems();
if (!\is_array($items)) {
return [];
}
return array_values(array_filter($items, static function ($it) {
return \is_object($it) && !empty($it->id);
}));
}
}

View File

@ -1,21 +0,0 @@
<?php
/**
* @version CVS: 1.0.0
* @package Com_Circolari
* @author Tommaso Cippitelli <tommaso.cippitelli@protocollicreativi.it>
* @copyright 2025 Tommaso Cippitelli
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Pcrt\Component\Circolari\Site\Service;
// No direct access
defined('_JEXEC') or die;
use \Joomla\CMS\Categories\Categories;
/**
* Content Component Category Tree
*
* @since 1.0.0
*/

View File

@ -21,9 +21,9 @@ class Router extends RouterView
$category->setKey('id')->setNestable();
$this->registerView($category);
// Child: circolare (lega la category tramite catid)
// Child: circolare (lega la category tramite categoria_id)
$circolare = new RouterViewConfiguration('circolare');
$circolare->setKey('id')->setParent($category, 'catid', 'id');
$circolare->setKey('id')->setParent($category, 'categoria_id', 'id');
$this->registerView($circolare);
parent::__construct($app, $menu);
@ -48,7 +48,7 @@ class Router extends RouterView
$path = (string) $db->setQuery(
$db->getQuery(true)
->select('path')
->from('#__categories')
->from('#__circolari_categorie')
->where('id = ' . $id)
->where("extension = 'com_content'")
)->loadResult();
@ -73,7 +73,7 @@ class Router extends RouterView
/* ---------------- PARSE ---------------- */
// Ricava catid accumulando i segmenti e risolvendo per PATH completo
// Ricava categoria_id accumulando i segmenti e risolvendo per PATH completo
public function getCategoryId($segment, $query)
{
static $segments = [];
@ -86,7 +86,7 @@ class Router extends RouterView
$id = (int) $db->setQuery(
$db->getQuery(true)
->select('id')
->from('#__categories')
->from('#__circolari_categorie')
->where('path = ' . $db->quote($path))
->where("extension = 'com_content'")
)->loadResult();
@ -99,7 +99,7 @@ class Router extends RouterView
$id = (int) $db->setQuery(
$db->getQuery(true)
->select('id')
->from('#__categories')
->from('#__circolari_categorie')
->where('alias = ' . $db->quote($segment))
->where("extension = 'com_content'")
->order('level DESC')
@ -108,10 +108,10 @@ class Router extends RouterView
return $id ?: 0;
}
// Alias circolare (+ catid già risolto) → id
// Alias circolare (+ categoria_id già risolto) → id
public function getCircolareId($segment, $query)
{
$catid = (int) ($query['catid'] ?? 0);
$categoria_id = (int) ($query['categoria_id'] ?? 0);
$db = Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
@ -119,8 +119,8 @@ class Router extends RouterView
->from('#__circolari')
->where('alias = ' . $db->quote($segment));
if ($catid > 0) {
$q->where('catid = ' . $catid);
if ($categoria_id > 0) {
$q->where('categoria_id = ' . $categoria_id);
}
$db->setQuery($q);

View File

@ -3,35 +3,50 @@ namespace Pcrt\Component\Circolari\Site\View\Circolare;
\defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Language\Text;
class HtmlView extends BaseHtmlView
{
protected $item;
public $items = [];
public $pagination;
public $state;
public function display($tpl = null)
{
// Carica la singola
$this->item = $this->get('Item');
if (!$this->item) {
throw new \Exception(Text::_('COM_CIRCOLARI_ITEM_NOT_FOUND'), 404);
}
// Se NON c'è l'item → mostra la lista
if (!$this->item) {
$app = \Joomla\CMS\Factory::getApplication();
$factory = $app->bootComponent('com_circolari')->getMVCFactory();
// Se il layout non c'è, stampa un fallback minimale (debug-friendly)
$tplPath = JPATH_COMPONENT_SITE . '/tmpl/circolare/default.php';
if (!is_file($tplPath)) {
$title = htmlspecialchars($this->item->title ?? 'Circolare');
$body = $this->item->description ?? $this->item->testo ?? $this->item->descrizione ?? '';
$html = '<article class="com-content-article item-page">'
. '<header class="page-header"><h1 class="page-title">'.$title.'</h1></header>'
. '<div class="article-body">'.$body.'</div>'
. '</article>';
/** @var \Pcrt\Component\Circolari\Site\Model\CircolariModel $model */
$model = $factory->createModel('Circolari', 'Site', ['ignore_request' => true]);
$this->document->setBuffer($html, 'component');
return;
}
// Imposta gli stati MANUALMENTE (niente populateState: è protected)
$model->setState('filter.categoria_id', 0); // nessun filtro categoria
$model->setState('list.start', 0);
$model->setState('list.limit', 0); // 0 = nessun limite (tutte)
$model->setState('list.ordering', 'c.id'); // colonna di ordinamento
$model->setState('list.direction', 'DESC'); // direzione
parent::display($tpl);
// Recupera i dati
$this->state = $model->getState();
$this->items = $model->getItems();
$this->pagination = $model->getPagination();
// Usa il layout della lista (tmpl/circolari/default.php)
$this->setLayout('default');
$this->addTemplatePath(JPATH_COMPONENT_SITE . '/tmpl/circolari');
return parent::display($tpl);
}
// Rendering normale della singola
return parent::display($tpl);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Pcrt\Component\Circolari\Site\View\Circolari;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
class HtmlView extends BaseHtmlView
{
public $items;
public $pagination;
public $state;
public function display($tpl = null)
{
$this->state = $this->get('State');
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
return parent::display($tpl);
}
}