This skill targets, in baserCMS 5.x (CakePHP 5-based),new development and modification of themes. Themes in 5.x arein plugin formatand,plugins/<ThemeName>/ and are composed of templates (templates/)・Asset (webroot/) and a small number of Helpers for front-end display (src/View/Helper/). For common rules such as environment, directories, naming, and the prohibition of core hacks, refer to basercms5-development; when giving a theme internal code equivalent to a plugin such as Controller/Table, refer to basercms5-plugin-development; and for migration work from 4.x themes, refer to basercms-theme-4-to-5-upgrade .
1. Structure of theme plugins
Create a theme with the following directory structure (core reference implementation: vendor/baserproject/bc-front/templates/, sample: plugins/BcThemeSample/)。
plugins/<ThemeName>/
├── config.php … テーマ情報(return 配列。'type' => 'Theme' 必須)
├── src/<ThemeName>Plugin.php … プラグインクラス(bake 生成の雛形でよい)
├── src/View/Helper/ … テーマ独自のフロント表示用 Helper(任意)
├── templates/
│ ├── layout/ … レイアウト(default.php 等)
│ ├── element/ … 共通エレメント(header.php / footer.php 等)
│ ├── element/widget/ … ウィジェット用 element(§5)
│ ├── Pages/ … 固定ページテンプレート(default.php 等)
│ ├── Blog/<content>/ … ブログテンプレート(posts.php / index.php / single.php / archives.php)
│ └── Mail/<content>/ … メールフォームテンプレート(index.php / confirm.php / submit.php)
└── webroot/ … css / js / img(テーマアセット)
config.phpはreturn ['type' => 'Theme', 'title' => ..., 'description' => ..., 'author' => ..., 'url' => ...];.- Layout
templates/layout/default.php, output the body with$this->fetch('content'), and inside<head>, immediately before$this->BcBaser->charset()/title()/metaDescription()/css()/js()/scripts()、</body>, assemble it using BcBaser APIs such as$this->BcBaser->func(). - The static page template
templates/Pages/default.phpoutputs the body edited in the admin panel withecho $page->contents($pageis a Page entity). - Per-content templates are placed as
templates/Blog/<コンテンツのテンプレート名>/posts.phpin the form "feature plugin name/template name/action name", and the directory for the template name selected in the content settings of the admin panel is used. Templates not present in the theme fall back to core (bc-front/templates/plugin/BcBlog/plugin/BcMail/, etc.). webroot/Assets under/テーマ名snake形式/css/style.css(plugin webroot delivery) (§4).- Apply the theme from the admin panel (Theme Management). To switch directly in a development environment,
sitestable'sthemecolumn is UPDATEd to clear the cache (bin/cake cache clear_all).
2. View and view variable conventions
In layout / element / templates, $this is the View itself. In Helpers, do not confuse it with $this->getView() (calling $this->getView() in a View calls Call to undefined method ...View::getView())。
Basically, do not depend on view variables supplied by the controller; instead,derive required values yourselfon the template side. The core does not supply global variables such as $user to all front-end views.
// ログインユーザー(UserInterface|false — false があり得るので必ず判定する)
$user = \BaserCore\Utility\BcUtil::loginUser();
$authPrefixes = $user ? $user->getAuthPrefixes() : [];
// 現在のプレフィックス(Front / Admin など)
$currentPrefix = \BaserCore\Utility\BcUtil::getRequestPrefix($this->getRequest());
// セッション
$session = $this->getRequest()->getSession();
Get the current content and site information fromrequest attributes. Both are entities, and since they may be null depending on context, reference them with ?-> .
$currentContent = $this->getRequest()->getAttribute('currentContent'); // Content エンティティ|null
$currentSite = $this->getRequest()->getAttribute('currentSite'); // Site エンティティ|null
$entityId = $currentContent?->entity_id; // 機能側レコードID(ブログコンテンツID 等)
$siteId = $currentSite?->id;
Write the current URL path as $this->getRequest()->getPath() (returned with a leading slash).
3. Helper
BcBaser delegation mechanism (PluginBaserHelper)
$this->BcBaser->blogPosts('news', 5) Feature shortcuts such as Bc<Plugin>BaserHelper::methods() are not methods of BcBaserHelper itself, but are provided viaMapping tablereturned by each plugin's __call (e.g., bc-blog's BcBlogBaserHelper::methods() に 'blogPosts' => ['Blog', 'posts'])。
- Existence is checked in two steps: ①
grep -rln "function <名前>" vendor/baserproject/to find the function definition → ② if not found,grep -rn "'<名前>'" vendor/baserproject/*/src/View/Helper/Bc*BaserHelper.phpでmethods()to find the mapping. Because of mapping delegation,having no function definition is normal, so do not judge "does not exist" based on ① alone. - The delegation target methodworks only when the providing plugin is enabled. When disabled, it results in BadMethodCall.
Commonly used static utilities
\BaserCore\Utility\BcSiteConfig::get('formal_name'); // サイト基本設定(address / name 等のキー)
\BaserCore\Utility\BcUtil::fullUrl($url); // フルURL生成
Dynamic loading of helpers
The helper is new , do not instantiate (with no DI container injected, getService() fails), but load from the View.loadHelper() returns an instance.
$blog = $this->getView()->loadHelper('BcBlog.Blog'); // Helper 内から
$blog = $this->loadHelper('BcBlog.Blog'); // テンプレート内($this が View)から
Reference name conventions and guards for dependencies on other plugins
- Core feature reference names are written with the
Bcprefix: helperBcBlog.Blog, tableTableRegistry::getTableLocator()->get('BcBlog.BlogPosts')、element$this->BcBaser->element('BcBlog.xxx'). The core table isplural + plugin prefix(BaserCore.Contents/BaserCore.Sites)。 - From the themeWhen calling another plugin's helper, guard with
Plugin::isLoaded()so the entire theme does not break when the plugin is disabled, and loadHelper if necessary.
if (\Cake\Core\Plugin::isLoaded('<Plugin>')) {
$helper = $this->helpers()->has('Sample')
? $this->Sample
: $this->loadHelper('<Plugin>.Sample');
$helper->show(['num' => 3]);
}
4. Assets and URLs
css() / js() signatures
BcBaser->css($path, $inline = true, $options = [])(js() likewise).The second argument is $inline(false buffer it with scripts() output with), andmedia attributes such asthird argumentare written in.
$this->BcBaser->css('style'); // <link ...>
$this->BcBaser->css(['print'], true, ['media' => 'print']); // 属性は第3引数
$this->BcBaser->js('theme', true, ['defer' => true]);
Do not hardcode URLs
URLs for links, images, etc. should be written through/about/ , etc.). The base is automatically added when installed in a subdirectory. BcBaser->getUrl('/path') rather than hardcoding root-relative paths (
<a href="<?php echo $this->BcBaser->getUrl('/about/') ?>">会社概要</a>
<?php $this->BcBaser->link('会社概要', '/about/') ?>
When assembling URL strings inside a Helper, either pass through \Cake\Routing\Router::url($url) or prefix the request's webroot: $this->getView()->getRequest()->getAttribute('webroot') . ltrim($url, '/')。
Displaying uploaded images (BcUploadHelper::uploadImage)
uploadImage('<field>', $entity, $options) write it as — the first argument is the plain field name,the second argument must be an EntityInterface(strings not allowed). BcUploadHelper resolves the table from the View's plugin/name by default, so when using it from a theme or another plugin's template, explicitly specify 'table' => '<Plugin>.<Tables>'(with BcUpload behavior) in options. The image size is 'imgsize', and the fallback when absent is 'noimage'. For mail form templates including file type, refer to setTableToUpload() in §6.
Delivery format for theme assets
Theme's webroot/ Underthe theme name in snake_case is delivered via URLs prefixed with.
$this->BcBaser->css('style'); // <ThemeName> の webroot/css/style.css を解決
// URL を直接組む場合
$this->BcBaser->getUrl('/sample_theme/css/style.css'); // plugins/SampleTheme/webroot/css/style.css
5. Widgets
Place the element to display in the widget area under templates/element/widget/(singular widget。widgets , not). BcWidgetArea searches this path (core placement example: bc-front/templates/plugin/BcBlog/element/widget/blog_recent_entries.php)。
- Even when using a widget provided by a plugin (e.g.,
BcBlog.widget/blog_recent_entries)をOverride in the theme, place it at the same path on the theme sidetemplates/element/widget/<名前>.php. - Place elements for admin panel widget settings under
templates/Admin/element/widget/.
6. Mail form templates
The mail form template is templates/Mail/<テンプレート名>/index.php(input),confirm.php(confirmation),submit.php(completion), and form rendering is done with BcBaser's mailForm-related APIs to write (BcMailBaserHelper::methods() bridges to the Mailform helper). The core source of truth is in vendor/baserproject/bc-front/templates/plugin/BcMail/ , so if in doubt, match that.
For form generation, take the entity $mailMessage set by the controller as the first argument, and specifyvalueSources .
$this->BcBaser->createMailForm($mailMessage, array_merge($options, ['valueSources' => ['context']]));
Main APIs (write field names as plain names. The context of theMailMessage. prefix is handled internally):
| Purpose | How to write |
|---|---|
| hidden field | $this->BcBaser->mailFormHidden('mode', ['id' => 'MailMessageMode']) |
| field unlock | $this->BcBaser->unlockMailFormField('mode') |
| Submit button | $this->BcBaser->mailFormSubmit($caption, $options) |
| Error display | $this->BcBaser->mailFormError('field_name', $message) |
| Image verification | $this->BcBaser->mailFormAuthCaptcha('auth_captcha', ['helper' => $this->BcBaser]) |
| Form end | $this->BcBaser->endMailForm() |
| freeze (confirm.php) | $this->BcBaser->freezeMailForm() |
| Field rendering | $this->BcBaser->mailFormControl($fieldName, $options) |
| label | $this->BcBaser->mailFormLabel($fieldName, $text) |
| Getting input values | $this->BcBaser->getMailFormSourceValue($fieldName) |
| Group last field determination | $this->BcBaser->isMailFormGroupLastField($mailFields, $field) |
mailFormHidden('mode')with a plain field name, the id refers tomode. JS#MailMessageModeif referring to['id' => 'MailMessageMode']explicitly.- At the beginning of templates that render file-type fields (index.php / confirm.php), always write the following one line. Without it, it fails with
BcUploadHelper を利用するには … table … を指定してください(BcException).
$this->BcBaser->setTableToUpload('BcMail.MailMessages');
- The
element('mail_input')that renders the field list falls back to the core version (bc-front/.../BcMail/element/mail_input.php), so if you do not need a custom design, you do not have to place it in the theme.
7. Blog templates
The blog template is templates/Blog/<テンプレート名>/ に index.php(list),archives.php(category/tag/year-month archive),single.php(detail),posts.php(BcBaser->blogPosts() parts list called from ).
$posts/$postはentity(or its ResultSet). Write associations with property access too:$post->title、$post->content、$post->detail、$post->blog_category->title、$post->blog_content_id。- The column name for the post date/time is
posted. Write sort specification as'sort' => 'posted'/orderBy(['BlogPosts.posted' => 'DESC']). - The featured image is
$this->Blog->getEyeCatch($post, ['width' => 300, 'link' => true])or BcBaser-delegated$this->BcBaser->blogPostEyeCatch($post, [...]). Check existence with$post->eye_catch. - Article links, dates, categories, and titles can also be written with BcBaser-delegated APIs:
getBlogPostLinkUrl($post)/blogPostDate($post, 'Y.m.d')/blogPostCategory($post)/blogPostTitle($post, false)/blogPostContent($post, true, false, 46)。 - Category lists and tag lists are rendered with the Blog helper/element (refer to the core
element/blog_category_list.php/blog_tag_list.php). - When fetching articles by querying the Table directly, write with the query builder from
TableRegistry::getTableLocator()->get('BcBlog.BlogPosts'), and usegetConditionAllowPublish()for publication conditions.
$blogPostsTable = \Cake\ORM\TableRegistry::getTableLocator()->get('BcBlog.BlogPosts');
$posts = $blogPostsTable->find()
->where($blogPostsTable->getConditionAllowPublish())
->where(['BlogPosts.blog_content_id' => $blogContentId])
->orderBy(['BlogPosts.posted' => 'DESC'])
->limit($num)
->all();
If the same kind of retrieval logic is scattered across multiple templates, consolidate it into the theme's src/View/Helper/<ThemeName>Helper.php .