アコーディオンUIを採用した、よくある質問(FAQ)ページの作成・続編です。
デバッグモードを設定しておくと、エラーが起きたときに詳細情報が表示されるようになります。
エラー箇所を探しやすくなるので、開発前に設定しておくのをオススメします。
デバッグモードの設定方法については 以下記事 で解説しています。
カスタマイズ後は、デバッグモードの解除を忘れないように。
『3』よくある質問をデータベースで管理する
前回までで表側のページを作成できたので、本記事では質問内容をデータベースに保存し、データベースから質問内容を取得・表示する方法を紹介します。それぞれの質問-答えに表示ステータスを設けることで、表示・非表示を切り替えられる設計にもなっています。
FAQテーブルの作成
「よくある質問&答え」を格納するテーブル(dtb_faq)をデータベースに作成します。
テーブルの作成方法については こちらの記事 をご覧ください。
まず、以下のFAQエンティティを作成して「app/Customize/Entity」にアップします。
<?php
namespace Customize\Entity;
use Eccube\Entity\AbstractEntity;
use Doctrine\ORM\Mapping as ORM;
/**
* FAQ
*
* @ORM\Table(name="dtb_faq")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
* @ORM\HasLifecycleCallbacks()
* @ORM\Entity(repositoryClass="Customize\Repository\FaqRepository")
*/
class FAQ extends AbstractEntity
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", options={"unsigned":true})
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="question", type="string", length=255)
*/
private $question;
/**
* @var string
*
* @ORM\Column(name="answer", type="text")
*/
private $answer;
/**
* @var bool
*
* @ORM\Column(name="status", type="boolean", nullable=false)
*/
private $status; // デフォルトで非表示状態に設定
/**
* @var \DateTime
*
* @ORM\Column(name="create_date", type="datetimetz")
*/
private $create_date;
/**
* @var int
*
* @ORM\Column(name="sort_no", type="integer", options={"unsigned":true})
*
* 並び順です。
*/
private $sort_no;
/**
* @var \DateTime
*
* @ORM\Column(name="update_date", type="datetimetz")
*/
private $update_date;
// Getters and Setters
public function getId(): ?int
{
return $this->id;
}
public function setQuestion(string $question): self
{
$this->question = $question;
return $this;
}
public function getQuestion(): ?string
{
return $this->question;
}
public function setAnswer(string $answer): self
{
$this->answer = $answer;
return $this;
}
public function getAnswer(): ?string
{
return $this->answer;
}
public function setStatus(bool $status = true): self
{
$this->status = $status;
return $this;
}
public function getStatus(): bool
{
return $this->status;
}
public function setCreateDate(\DateTime $createDate): self
{
$this->create_date = $createDate;
return $this;
}
public function getCreateDate(): ?\DateTime
{
return $this->create_date;
}
public function setUpdateDate(\DateTime $updateDate): self
{
$this->update_date = $updateDate;
return $this;
}
public function getUpdateDate(): ?\DateTime
{
return $this->update_date;
}
public function setSortNo($sortNo)
{
$this->sort_no = $sortNo;
return $this;
}
public function getSortNo()
{
return $this->sort_no;
}
}
続いてサーバーにSSH接続し、キャッシュを削除してからSQLを実行し、データベースに反映します。
bin/console cache:clear --no-warmup
bin/console doctrine:schema:update --dump-sql --force
コマンド実行後、問題がなければ新規テーブル「dtb_faq」が追加されます。
FAQリポジトリの作成
「dtb_faq」に保存されているデータを取得し、Twigテンプレートに渡すためのメソッドを記述したリポジトリ(以下)を作成します。作成したリポジトリは「app/Customize/Repository」にアップします。
<?php
namespace Customize\Repository;
use Customize\Entity\FAQ;
use Eccube\Repository\AbstractRepository;
use Doctrine\Persistence\ManagerRegistry as RegistryInterface;
/**
* FaqRepository
*/
class FaqRepository extends AbstractRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, FAQ::class);
}
public function findActiveFaqs()
{
return $this->createQueryBuilder('cn')
->andWhere('cn.status = :status')
->setParameter('status', true)
->orderBy('cn.sort_no', 'DESC')
->getQuery()
->getResult();
}
}
このリポジトリではfindActiveFaqs()
というメソッドを定義しています。表示ステータスがtrue
のデータのみを、ソート番号が大きい順(DESC = 降順)に取得するクエリを記述しています。
FAQコントローラの修正
STEP 2で作成したリポジトリを使い、「dtb_faq」テーブルに保存したデータを取得してTwigテンプレートに渡すようコードを修正します。
<?php
namespace Customize\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Eccube\Controller\AbstractController;
use Customize\Repository\FaqRepository;
class FaqController extends AbstractController
{
/**
* @var FaqRepository
*/
protected $faqRepository;
/**
* @param FaqRepository $faqRepository
*/
public function __construct(FaqRepository $faqRepository)
{
$this->faqRepository = $faqRepository;
}
/**
* @Route("/faq", name="faq")
* @Template("faq.twig")
*/
public function index(Request $request)
{
$faqs = $this->faqRepository->findActiveFaqs();
return ['faqs' => $faqs,];
}
}
faq.twigテンプレートの修正
最後に、コントローラから渡されたデータが表示されるようTwigテンプレートを修正します。
{% extends 'default_frame.twig' %}
{% block stylesheet %}
<style>
dl {
margin: 0;
}
dl > div {
margin-bottom: 8px;
}
dt {
padding: 8px;
cursor: pointer;
user-select: none;
position: relative;
}
dt:hover {
background-color: rgba(0, 0, 0, .1);
}
dt::before {
content: 'Q. ';
}
dt::after {
content: '+';
position: absolute;
top: 8px;
right: 16px;
transition: transform .3s;
}
dl > div > div.appear dt::after {
transform: rotate(45deg);
}
dd {
padding: 8px;
margin: 0;
display: none;
}
dl > div > div.appear dd {
display: block;
animation: .3s fadeIn;
}
.ec-off1Grid {
margin-bottom: 8px;
}
@keyframes fadeIn {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: none;
}
}
</style>
{% endblock stylesheet %}
{% block javascript %}
<script>
const dts = document.querySelectorAll('dt');
dts.forEach(dt => {
dt.addEventListener('click', () => {
dt.parentNode.classList.toggle('appear');
});
});
</script>
{% endblock javascript %}
{% block main %}
<div class="ec-role">
<div class="ec-pageHeader">
<h1>{{ 'よくある質問'|trans }}</h1>
</div>
<dl>
{% for faq in faqs %}
<div class="ec-off1Grid">
<div class="ec-off1Grid__cell">
<dt>{{ faq.question }}</dt>
<dd>{{ faq.answer }}</dd>
</div>
</div>
{% endfor %}
</dl>
</div>
{% endblock %}
かなりコードがスッキリしました。あとは、「dtb_faq」に質問-回答を保存&表示ステータスをtrueにしていけば、作成日順に質問が表示されるはずです!
次のページでは、管理画面からよくある質問の登録や編集が行えるようにしていきます。