この記事は、Magento2でエクステンションを作る方法(2)の続きです。
前回の記事はこちら「グリッドを表示

管理画面から画像を保存し、それをフロントエンドにウィジェットを使って表示するエクステンションを作りたいと思います。
ウィジェットを使うことで、フロントの好きな場所に画像を表示することができます。

全体的なイメージ

DBのイメージ

  • テーブル名:HelloImg
  • カラム名:id, title, img

タイトルと画像ファイルを保存

まず、右上のAdd New Rowボタンを実装したいと思います。

Veriteworks/Helloimg/view/adminhtml/layout/helloimg_grid_addrow.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <link src="Magento_Customer::js/bootstrap/customer-post-action.js"/>
    </head>
    <body>
        <referenceContainer name="content">
            <block class="Veriteworks\Helloimg\Block\Adminhtml\Helloimg\AddRow" name="add_row" />
        </referenceContainer>
    </body>
</page>

次に、このレイアウトxmlに対応するControllerとBlockを作成します。

Veriteworks/Helloimg/Controller/Adminhtml/Grid/AddRow.php

<?php

namespace Veriteworks\Helloimg\Controller\Adminhtml\Grid;

use Magento\Framework\Controller\ResultFactory;
use Veriteworks\Helloimg\Model\GridFactory;

class AddRow extends \Magento\Backend\App\Action
{
    /**
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\Registry    $coreRegistry
     */
    protected $_storeManager;
    protected $_gridFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        GridFactory $gridFactory
    )
    {
        parent::__construct($context);
        $this->_coreRegistry = $coreRegistry;
        $this->_gridFactory = $gridFactory;
    }
    /**
     * Add New Row Form page.
     *
     * @return \Magento\Backend\Model\View\Result\Page
     */
    public function execute()
    {
//        $websites=$this->_storeManager->getWebsites();

        $rowId = (int) $this->getRequest()->getParam('id');
        $rowData = $this->_gridFactory->create();
        if ($rowId) {
            $rowData = $rowData->load($rowId);
            $rowTitle = $rowData->getTitle();
            if (!$rowData->getImgId()) {
                $this->messageManager->addError(__('row data no longer exist.'));
                $this->_redirect('helloimg/grid/');
                return;
            }
        }

        $this->_coreRegistry->register('row_data', $rowData);
        $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        $title = $rowId ? __('Edit Row Data ').$rowTitle : __('Add Row Data');
        $resultPage->getConfig()->getTitle()->prepend($title);
        return $resultPage;
    }
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Veriteworks_Helloimg::add_row');
    }
}

Veriteworks/Helloimg/Block/Helloimg/AddRow.php

<?php

namespace Veriteworks\Helloimg\Block\Adminhtml\Helloimg;

class AddRow extends \Magento\Backend\Block\Widget\Form\Container
{
    /**
     * Core registry.
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry = null;

    /**
     * @param \Magento\Backend\Block\Widget\Context $context
     * @param \Magento\Framework\Registry           $registry
     * @param array                                 $data
     */
    public function __construct(
        \Magento\Backend\Block\Widget\Context $context,
        \Magento\Framework\Registry $registry,
        array $data = []
    )
    {
        $this->_coreRegistry = $registry;
        parent::__construct($context, $data);
    }

    /**
     * Initialize Imagegallery Images Edit Block.
     */
    protected function _construct()
    {
        $this->_objectId = 'row_id';
        $this->_blockGroup = 'Veriteworks_Helloimg';
        $this->_controller = 'adminhtml_helloimg';
        parent::_construct();
        if ($this->_isAllowedAction('Veriteworks_Helloimg::add_row')) {
            $this->buttonList->update('save', 'label', __('Save'));
        } else {
            $this->buttonList->remove('save');
        }
        $this->buttonList->remove('reset');
    }

    /**
     * Retrieve text for header element depending on loaded image.
     *
     * @return \Magento\Framework\Phrase
     */
    public function getHeaderText()
    {
        return __('Add RoW Data');
    }

    /**
     * Check permission for passed action.
     *
     * @param string $resourceId
     *
     * @return bool
     */
    protected function _isAllowedAction($resourceId)
    {
        return $this->_authorization->isAllowed($resourceId);
    }

    /**
     * Get form action URL.
     *
     * @return string
     */
    public function getFormActionUrl()
    {
        if ($this->hasFormActionUrl()) {
            return $this->getData('form_action_url');
        }

        return $this->getUrl('*/*/save');
    }
}

Veriteworks/Helloimg/Block/Helloimg/Edit/Form.php

  • Blockは、フロントエンドとイメージすると分かりやすいです。
  • ここで、addページに表示する項目を出力しています。
<?php

namespace Veriteworks\Helloimg\Block\Adminhtml\Helloimg\Edit;

/**
 * Adminhtml Add New Row Form.
 */
class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
    /**
     * @var \Magento\Store\Model\System\Store
     */
    protected $_systemStore;

    /**
     * @param \Magento\Backend\Block\Template\Context $context
     * @param \Magento\Framework\Registry             $registry
     * @param \Magento\Framework\Data\FormFactory     $formFactory
     * @param array                                   $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Data\FormFactory $formFactory,
        \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig,
        array $data = []
    )
    {
        $this->_wysiwygConfig = $wysiwygConfig;
        parent::__construct($context, $registry, $formFactory, $data);
    }

    /**
     * Prepare form.
     *
     * @return $this
     */
    protected function _prepareForm()
    {
        $dateFormat = $this->_localeDate->getDateFormat(\IntlDateFormatter::SHORT);
        $model = $this->_coreRegistry->registry('row_data');
        $form = $this->_formFactory->create(
            ['data' => [
                'id' => 'edit_form',
                'enctype' => 'multipart/form-data',
                'action' => $this->getData('action'),
                'method' => 'post'
            ]
            ]
        );

        $form->setHtmlIdPrefix('helloimg_');
        if ($model->getImgId()) {
            $fieldset = $form->addFieldset(
                'base_fieldset',
                ['legend' => __('Edit Row Data'), 'class' => 'fieldset-wide']
            );
            $fieldset->addField('img_id', 'hidden', ['name' => 'img_id']);
        } else {
            $fieldset = $form->addFieldset(
                'base_fieldset',
                ['legend' => __('Add Row Data'), 'class' => 'fieldset-wide']
            );
        }

       // $wysiwygConfig = $this->_wysiwygConfig->getConfig(['tab_id' => $this->getTabId()]);


        $fieldset->addField(
            'title',
            'text',
            [
                'name' => 'title',
                'label' => __('Title'),
                'id' => 'title',
                'title' => __('Title'),
                'class' => 'required-entry',
                'required' => true,
            ]
        );
        $fieldset->addField(
            'image',
            'image',
            [
                'name' => 'image',
                'label' => __('Image'),
                'id' => 'image',
                'title' => __('Image'),
                'class' => 'required-entry',
                'required' => true,
            ]
        );

        $form->setValues($model->getData());
        $form->setUseContainer(true);
        $this->setForm($form);

        return parent::_prepareForm();
    }
}

ここでページを確認してみると、ちゃんとaddページが出力されています。

しかし、まだSave Controllerを作成していないため、保存はできません。
次に、Save Controllerを作成します。

Veriteworks\Helloimg\Controller\Adminhtml\Grid\Save.php

<?php

namespace Veriteworks\Helloimg\Controller\Adminhtml\Grid;

use Veriteworks\Helloimg\Model\GridFactory;

class Save extends \Magento\Backend\App\Action
{
    /**
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    protected $_gridFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        GridFactory $gridFactory
    )
    {
        $this->_gridFactory = $gridFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $data = $this->getRequest()->getPostValue();
        if (!$data) {
            $this->messageManager->addError(__('Problem on saving. Post data is empty.'));
            $this->_redirect('helloimg/grid/index');
            return;
        }
        try {
            $rowData = $this->_gridFactory->create();
            $rowData->setData($data);
            if (isset($data['id'])) {
                $rowData->setImgId($data['id']);
            }
            $rowData->save();
            $this->messageManager->addSuccess(__('Row data has been successfully saved.'));
        } catch (Exception $e) {
            $this->messageManager->addError(__($e->getMessage()));
        }
        $this->_redirect('helloimg/grid/index');
    }

    /**
     * Check Category Map permission.
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Veriteworks_Helloimg::add_save');
    }
}

ここで、キャッシュを消して保存をしてみると、保存できました。
次に、タイトルだけではなく、画像も保存してみたいと思います。

先ほど作成したSave.phpに追記していきます。

Veriteworks\Helloimg\Controller\Adminhtml\Grid\Save.php

  • ドキュメントルート/pub/media/veriteworks/helloimgに画像は保存されます。
<?php

namespace Veriteworks\Helloimg\Controller\Adminhtml\Grid;

use Veriteworks\Helloimg\Model\GridFactory;

class Save extends \Magento\Backend\App\Action
{
    /**
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    protected $_gridFactory;
    protected $_uploader;
    protected $_adapterFactory;
    protected $_filesystem;


    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\MediaStorage\Model\File\UploaderFactory $_uploader,
        \Magento\Framework\Image\AdapterFactory $_adapterFactory,
        \Magento\Framework\Filesystem $_filesystem,
        GridFactory $gridFactory
    )
    {
        $this->_gridFactory = $gridFactory;
        $this->_uploader = $_uploader;
        $this->_adapterFactory = $_adapterFactory;
        $this->_filesystem = $_filesystem;

        parent::__construct($context);
    }

    public function execute()
    {
        $data = $this->getRequest()->getPostValue();
        if (!$data) {
            $this->messageManager->addError(__('Problem on saving. Post data is empty.'));
            $this->_redirect('helloimg/grid/index');
            return;
        }
        try {
            $rowData = $this->_gridFactory->create();
            if (isset($data['id'])) {
                $rowData->setImgId($data['id']);
            }
            if (isset($_FILES['image']) && isset($_FILES['image']['name']) && strlen($_FILES['image']['name'])) {
                $base_media_path = 'veriteworks/helloimg/images';
                $_uploader = $this->_uploader->create(
                    ['fileId' => 'image']
                );
                $_uploader->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']);
                $imageAdapter = $this->_adapterFactory->create();
                $_uploader->addValidateCallback('image', $imageAdapter, 'validateUploadFile');
                $_uploader->setAllowRenameFiles(true);
                $_uploader->setFilesDispersion(true);
                $mediaDirectory = $this->_filesystem->getDirectoryRead(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA);
                $result = $_uploader->save(
                    $mediaDirectory->getAbsolutePath($base_media_path));
                $data['image'] = $base_media_path . $result['file'];
            } else {
                if (isset($data['image']) && isset($data['image']['value'])) {
                    if (isset($data['image']['delete'])) {
                        $data['image'] = '';
                        $data['delete_image'] = true;
                    } elseif (isset($data['image']['value'])) {
                        $data['image'] = $data['image']['value'];
                    } else {
                        $data['image'] = '';
                    }
                }
            }
            $rowData->setData($data);
            $rowData->save();
            $this->messageManager->addSuccess(__('Row data has been successfully saved.'));
        } catch (Exception $e) {
            $this->messageManager->addError(__($e->getMessage()));
        }
        $this->_redirect('helloimg/grid/index');
    }

    /**
     * Check Category Map permission.
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Veriteworks_Helloimg::add_save');
    }
}

画像が保存できるようになりました。
実際に、DBや管理画面から画像が保存されているか、削除できるか、編集できるかなどを確認してみてください。

次に、グリッド上に保存した画像を表示させたいと思います。
グリッドの表示を変更したいので、helloimg_record_grid_list.xmlに追記をします。

Veriteworks/Helloimg/view/adminhtml/layout/helloimg_record_grid_list.xml

  • #152行目に以下を追記します。
<column name="image" class="Veriteworks\Helloimg\Ui\Component\Listing\Grid\Column\Image">
            <argument name="data" xsi:type="array">
                <item name="width" xsi:type="string">150px</item>
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item>
                    <item name="bodyTmpl" xsi:type="string">ui/grid/cells/html</item>
                    <item name="sortable" xsi:type="boolean">false</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="align" xsi:type="string">left</item>
                    <item name="label" xsi:type="string" translate="true">Image</item>
                </item>
            </argument>
        </column>

画像を表示する列を追加しました。
次に、これに必要なクラスを作成していきます。

Veriteworks\Helloimg\Ui\Component\Listing\Grid\Column\Image.php

  • prepareItemメソッドで、グリッドに表示する画像サイズを調整します。
<?php

namespace Veriteworks\Helloimg\Ui\Component\Listing\Grid\Column;

use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Framework\View\Element\UiComponent\ContextInterface;

class Image extends \Veriteworks\Helloimg\Ui\Component\Listing\Grid\Column\AbstractColumn
{
    /**
     * default width and height image.
     */
    const IMAGE_WIDTH = '70%';
    /**
     *
     */
    const IMAGE_HEIGHT = '60';
    /**
     *
     */
    const IMAGE_STYLE = 'display: block;margin: auto;';

    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $_storeManager;
    /**
     * @var
     */
    protected $logger;

    /**
     * Constructor.
     *
     * @param ContextInterface   $context
     * @param UiComponentFactory $uiComponentFactory
     * @param array              $components
     * @param array              $data
     */
    public function __construct(
        ContextInterface $context,
        UiComponentFactory $uiComponentFactory,
        \Magento\Framework\Filesystem $filesystem,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        array $components = [],
        array $data = []
    ) {
        parent::__construct($context, $uiComponentFactory, $components, $data);
        $this->_storeManager = $storeManager;
    }

    /**
     * prepare item.
     *
     * @param array $item
     *
     * @return array
     */

    protected function _prepareItem(array & $item)
    {
        $width = $this->hasData('width') ? $this->getWidth() : self::IMAGE_WIDTH;
        $height = $this->hasData('height') ? $this->getHeight() : self::IMAGE_HEIGHT;
        $style = $this->hasData('style') ? $this->getStyle() : self::IMAGE_STYLE;
        if (isset($item[$this->getData('name')])) {
            if ($item[$this->getData('name')]) {
                $srcImage = $this->_storeManager->getStore()
                        ->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA) . $item[$this->getData('name')];
                $item[$this->getData('name')] = sprintf(
                    '<img src="%s"  width="%s" height="%s" style="%s" />',
                    $srcImage,
                    $width,
                    $height,
                    $style
                );
            } else {
                $item[$this->getData('name')] = '';
            }
        }

        return $item;
    }
}

次に、継承クラスを作成します。

Veriteworks\Helloimg\Ui\Component\Listing\Grid\Column\AbstractColumn.php

<?php

namespace Veriteworks\Helloimg\Ui\Component\Listing\Grid\Column;

abstract class AbstractColumn extends \Magento\Ui\Component\Listing\Columns\Column
{
    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as &$item) {
                $this->_prepareItem($item);
            }
        }

        return $dataSource;
    }

    abstract protected function _prepareItem(array & $item);
}

グリッドを確認してみると、画像が表示されていると思います。

タイトルと画像ファイルを保存するところまで、終わりました。
次回は、保存した画像ファイルを表示するウィジェットを作成です。