私が書いている私の学習コンポーネントを拡張したいと思います。このため、IMAPからメールを取得し、特定のファイルタイプ(png、jpg、...のみ)がある場合に添付ファイルを保存する小さなチケットシステムを作成しています。
添付ファイルを保存する間、ファイル名はハッシュされ、後で保存されます。
もちろん、チケットビューも拡張したいと思います。チケットビューには、このチケットに割り当てられたすべてのメッセージがあります(使用されているモデル:チケット、メッセージ)。
次に、1つ以上の添付ファイルがあるこのチケットの各メッセージのダウンロード可能な添付ファイルを表示したいと思います。 「添付ファイルのダウンロード」をクリックすると、ダウンロード処理が開始されます。
左結合でメッセージモデルを拡張しようとしましたが、うまくいきません。
これは私のコード(モデル)です:
<?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
結果の出力は次のようになります。
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"
}
次に、ファイル名とファイルパスを関連付けて、次のように使用します。
foreach($message->attachments as $attachment)
{
$attachment->filename;
$attachment->filepath;
}
どうすればこの問題を解決できますか?
あなたが言及したオブジェクトをコードのどこに出力しているのかわかりません。通常、getListQueryによって生成されたクエリは配列になります。オブジェクトは配列内に含まれている可能性があるものの例だと思いますか?
もしそうなら、私はあなたのグループ連結を次のように置き換えます
$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');
それはあなたに何かを与えるでしょう
["attachments"]=> string(217) "Test.png|asdaadasasgsdafasdasdasgasfasf, Bildschirmfoto 2015-07-14 um 17.10.43.png|52d0833774bc94698e4101c021deebecf5724a4c65d1efc477948180c072c61778b6c0174040cdef6d94d8669251f0a6646c5caf6e1966b9796625979973b2a4.png"
他の場所にいるとしましょう:
$query = $this->getListQuery();
$rows = $db->setQuery($query)->loadObjectList();
次のように、これらの行を反復処理できます。
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;
}
安全のために、2つの連結区切り文字 '|'も置き換えますと '、'を文字列に使用すると、ファイル名/パスに存在しないことが確実になります。たとえば、fabrikでは '//..//'のようなものを使用します
余談ですが、なぜgetListQueryを使用してチケットモデルをロードしているのか、また使用されていないように見えますか?
別の方法は、グループ連結をまとめてダンプし、2つの別々のクエリとして実行することです。$ tickets(オブジェクトの配列)としてチケットのリストを取得したとします。
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());
}
これはわずかに遅いかもしれませんが、より読みやすくなっています。
GROUP_CONCATはパフォーマンスキラーです。言うまでもなく、クエリでは、フィルターする前に行を結合する可能性があります。そのため、より大きなデータセットでは多くのメモリとCPU時間を浪費します。
私はこれを削除します:
$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');
添付ファイルをバインドする関数を作成します。
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();
}
}
}
そして、次のようにgetItems
関数をオーバーライドします。
public function getItems() {
$items = parent::getItems();
$this->bindAttachments($items);
return $items;
}