it-swarm.com.de

Das Modell sollte Daten aus einer anderen Tabelle laden und ein assoziatives Array erstellen

Ich möchte meine Lernkomponente, die ich schreibe, erweitern. Aus diesem Grund erstelle ich ein kleines Ticketing-System, das E-Mails aus IMAP abruft und Anhänge speichert, wenn sie einen bestimmten Dateityp haben (nur PNG, JPG, ...).

Beim Speichern der Anhänge wird der Dateiname gehasht und anschließend gespeichert.

Natürlich möchte ich auch meine Ticket-Ansicht erweitern: In meiner Ticket-Ansicht sind alle Nachrichten diesem Ticket zugeordnet (verwendete Modelle: Ticket, Nachrichten).

Jetzt möchte ich für jede Nachricht in diesem Ticket, die einen oder mehrere Anhänge enthält, herunterladbare Anhänge anzeigen. Klicken Sie auf "Anhang herunterladen", um den Downloadvorgang zu starten.

Ich habe versucht, das Nachrichtenmodell mit dem Linken Join zu erweitern, aber es funktioniert nicht so gut.

Dies ist mein Code (Modell):

<?php

    defined('_JEXEC') or die;

    class BestiaModelMessages extends JModelList
    {

        /**
         * __construct function.
         * 
         * @access public
         * @param array $config (default: array())
         * @return void
         */
        public function __construct($config = array())
        {

            if (empty($config['filter_fields']))
            {
                $config['filter_fields'] = array(
                                                'id', 'a.id',
                                                'ticketid' , 'a.ticketid',
                                                'from', 'a.from',
                                                'to', 'a.to',
                                                'date', 'a.date',
                                                'content', 'a.content',
                                                'title', 'a.title'
                                                ); 
            }   

            parent::__construct($config);
        }

        /**
         * populateState function.
         * 
         * @access protected
         * @param mixed $ordering (default: null)
         * @param mixed $direction (default: null)
         * @return void
         */
        protected function populateState($ordering = null, $direction = null)
        {
            $published = $this->getUserStateFromRequest($this->context.'.filter.state', 'filter_state', '', 'string');
            $this->setState('filter.state', $published);
            parent::populateState('a.id', 'desc');      
        }

        /**
         * getListQuery function.
         * 
         * @access protected
         * @return void
         */
        protected function getListQuery()
        {
            $db    = $this->getDbo();
            $query  = $db->getQuery(true);
            $query->select(
            $this->getState(
                            'list.select',
                            'a.id, a.ticketid, a.from, a.to, a.date, a.title, a.content'));

            $query->from($db->quoteName('#__bestia_tickets_messages').' AS a');

            // Filter by search in title
            $search = $this->getState('filter.search');

            if (!empty($search))
            {

                if (stripos($search, 'id:') === 0)
                {
                    $query->where('a.id = ' . (int) substr($search, 3));
                }

                else
                {
                    $search = $db->quote('%' . $db->escape($search, true) . '%');
                    $query->where('(a.content LIKE ' . $search.' OR a.id LIKE ' . $search . 'OR a.from LIKE ' . $search . 'OR a.to LIKE ' . $search . 'OR a.date LIKE ' . $search .' OR a.title LIKE ' . $search . ')');
                }

            }           


            $query->select('GROUP_CONCAT(cast(concat(tma.filename,\', \',tma.filepath)AS char) SEPARATOR \', \') AS attachments')
                  ->join('LEFT', '#__bestia_tickets_messages_attachments as tma ON a.id = tma.messageid');



            $model =& $this->getInstance('Ticket', 'BestiaModel');          
            $item = $model->getItem();
            $ticketid = $item->ticketid;

            $query->where('a.ticketid = '.$db->quote($ticketid));
            $query->order('a.date DESC');  
            $query->group('a.id');

//          var_dump((string)$query);

           return $query;
         } // ./protected function getListQuery

    } // ./class BestiaModelMessages

Die resultierende Ausgabe sieht folgendermaßen aus:

object(stdClass)#375 (8) { 
    ["id"]=> string(3) "725" 
    ["ticketid"]=> string(11) "123456789" 
    ["from"]=> string(16) "[email protected]" 
    ["to"]=> string(27) "[email protected]" 
    ["date"]=> string(19) "2015-08-03 14:47:27" 
    ["title"]=> string(17) "Ticket mit Anhang" 
    ["content"]=> string(775) "Ticket mit Anhang…" 
    ["attachments"]=> string(217) "Test.png, asdaadasasgsdafasdasdasgasfasf, Bildschirmfoto 2015-07-14 um 17.10.43.png, 52d0833774bc94698e4101c021deebecf5724a4c65d1efc477948180c072c61778b6c0174040cdef6d94d8669251f0a6646c5caf6e1966b9796625979973b2a4.png" 
}

Jetzt möchte ich den Dateinamen und den Dateipfad so zuordnen:

foreach($message->attachments as $attachment)
{
     $attachment->filename;
     $attachment->filepath;
}

Wie könnte ich dieses Problem lösen?

1
MyFault

Ich sehe nicht, wo in Ihrem Code Sie das von Ihnen erwähnte Objekt ausgeben. Normalerweise sollte die von getListQuery erzeugte Abfrage zu einem Array führen. Ich nehme an, dass Objekt ein Beispiel für ein Objekt ist, das im Array enthalten sein könnte.

Wenn ja, dann würde ich Ihr Gruppenkonzert wie folgt ersetzen

$query->select('GROUP_CONCAT(cast(concat(tma.filename,\'|\',tma.filepath)AS char) SEPARATOR \',\') AS attachments')
              ->join('LEFT', '#__bestia_tickets_messages_attachments as tma ON a.id = tma.messageid');

das würde dir sowas geben

["attachments"]=> string(217) "Test.png|asdaadasasgsdafasdasdasgasfasf, Bildschirmfoto 2015-07-14 um 17.10.43.png|52d0833774bc94698e4101c021deebecf5724a4c65d1efc477948180c072c61778b6c0174040cdef6d94d8669251f0a6646c5caf6e1966b9796625979973b2a4.png" 

Sagen Sie also, Sie haben woanders:

$query = $this->getListQuery();
$rows = $db->setQuery($query)->loadObjectList();

sie können diese Zeilen nun wie folgt durchlaufen:

foreach ($rows as &$row)
{
 $attachments = explode(',' $row->attachments);
 $res = array();

  foreach ($attachments as $attachment) 
  {
    $attachment = explode('|', $attachment);
    $obj = new stdClass; 
    $obj->filename = $attachment[0];
    $obj->filepath = $attachment[1];
    $res[] = $obj;
  }

  $row->attachments = $res;
}

Aus Sicherheitsgründen würde ich auch die beiden Konkatabscheider '|' ersetzen. und ',' mit einem String, von dem Sie sicher sind, dass er nicht im Dateinamen/Pfad enthalten ist. Zum Beispiel verwenden wir in der Fabrik so etwas wie '//..//'

Abgesehen davon bin ich mir nicht sicher, warum Sie das Tickets-Modell mit der getListQuery laden, und es scheint nicht verwendet zu werden?

Eine alternative Methode wäre, das Gruppen-Concat zu löschen und es als zwei separate Abfragen auszuführen: Nehmen wir an, Sie haben die Liste der Tickets als $ tickets (eine Reihe von Objekten).

use Joomla\Utilities\ArrayHelper;
$ids = ArrayHelper::getColumn($tickets, 'id');
$ids = ArrayHelper::toInteger($ids);

if (!empty($ids)) 
{
  $query = $db->getQuery(true);
  $query->select('filename, filepath')
  ->from('#__bestia_tickets_messages_attachments')
 ->where('ticket_id IN (' . implode(',', $ids) . ')');
  $db->setQuery($query);
  $attachments = $db->loadObjectList('ticket_id');
}

foreach ($tickets as &$ticket) 
{
  $ticket->attachments = ArrayHelper::getValue($attachments, $ticket->id, array());
}

Dies mag etwas langsamer sein, ist aber besser lesbar.

2
Rob Clayburn

GROUP_CONCAT ist ein Leistungskiller. Ganz zu schweigen davon, dass es in Ihrer Abfrage möglich ist, dass Zeilen verknüpft werden, bevor sie gefiltert werden. Dadurch wird viel Speicher und CPU-Zeit für größere Datensätze verschwendet.

Ich würde dies entfernen:

$query->select('GROUP_CONCAT(cast(concat(tma.filename,\', \',tma.filepath)AS char) SEPARATOR \', \') AS attachments')
              ->join('LEFT', '#__bestia_tickets_messages_attachments as tma ON a.id = tma.messageid');

Funktion zum Binden von Anhängen erstellen:

protected function bindAttachments(Array &$items) {
    // Get messages ids
    $ids = array();
    foreach( $items AS $item ) {
        $ids[] = (int)$item->id;
    }

    // Get attachments
    $query = $this->_db->getQuery(true);
    $query->select('*');
    $query->from('#__bestia_tickets_messages_attachments');
    $query->where('messageid IN('.implode(',', $ids).')');
    $query->order('messageid ASC');

    $this->_db->setQuery($query);
    $attachments = $this->_db->loadObjectList();

    // Create attachments map by ID
    $attachments_map = array();
    foreach( $attachments AS $attachment ) {
        if( !isset($attachments_map[$attachment->messageid]) ) {
            $attachments_map[$attachment->messageid] = array();
        }

        $attachments_map[$attachment->messageid][] = $attachment;
    } 

    // Attach attachments to items array
    foreach( $items AS &$item ) {
        if( isset($attachments_map[$item->id]) ) {
            $item->attachments = $attachments_map[$item->id];
        } else {
            $item->attachments = array();
        }
    }
}

Und jetzt überschreiben Sie einfach Ihre getItems -Funktion wie folgt:

public function getItems() {
    $items = parent::getItems();
    $this->bindAttachments($items);

    return $items;
}
1
Artur Stępień