Skip to content
  1. Extras
  2. MiniShop3
  3. Frontend interface
  4. Customer account
  5. Customer profile

Customer profile

The profile page lets the customer view and edit personal data. It is part of the MiniShop3 customer account.

Page structure

ComponentFilePurpose
Base layouttpl.msCustomer.baseCommon wrapper with sidebar
Sidebartpl.msCustomer.sidebarAccount navigation
Profiletpl.msCustomer.profileEdit form

Snippet call

fenom
{'!msCustomer' | snippet : [
    'service' => 'profile'
]}

Parameters

ParameterDefaultDescription
serviceprofileService type
tpltpl.msCustomer.profileProfile chunk
unauthorizedTpltpl.msCustomer.unauthorizedChunk for guests
returntplFormat: tpl or data

Chunk architecture

Customer account chunks use inheritance:

tpl.msCustomer.base          — base layout
├── {include 'tpl.msCustomer.sidebar'}  — sidebar
└── {block 'content'}        — content area
    └── tpl.msCustomer.profile — profile form

Placeholders

In tpl.msCustomer.profile

PlaceholderTypeDescription
{$customer}arrayCustomer data
{$customer.id}intCustomer ID
{$customer.email}stringEmail
{$customer.first_name}stringFirst name
{$customer.last_name}stringLast name
{$customer.phone}stringPhone
{$email_verified}boolEmail verified
{$email_verified_at}stringEmail verification date
{$phone_verified}boolPhone verified
{$phone_verified_at}stringPhone verification date
{$errors}arrayValidation errors
{$success}boolSave succeeded

Profile chunk

fenom
{* tpl.msCustomer.profile *}
{extends 'tpl.msCustomer.base'}

{block 'content'}
<div class="ms3-customer-profile">
    <div class="card shadow-sm">
        <div class="card-header bg-primary text-white">
            <h5 class="mb-0">{'ms3_customer_profile_title' | lexicon}</h5>
        </div>
        <div class="card-body">
            {if $success?}
            <div class="alert alert-success" role="alert">
                {'ms3_customer_profile_updated' | lexicon}
            </div>
            {/if}

            <form class="ms3_form ms3-customer-profile-form" method="post" action="">
                <input type="hidden" name="ms3_action" value="customer/update-profile">

                <div class="row">
                    {* First name *}
                    <div class="col-md-6 mb-3">
                        <label for="first_name" class="form-label">
                            {'ms3_customer_first_name' | lexicon} <span class="text-danger">*</span>
                        </label>
                        <input type="text"
                               class="form-control {if $errors.first_name?}is-invalid{/if}"
                               id="first_name"
                               name="first_name"
                               value="{$customer.first_name}"
                               required>
                        {if $errors.first_name?}
                        <div class="invalid-feedback">{$errors.first_name}</div>
                        {/if}
                    </div>

                    {* Last name *}
                    <div class="col-md-6 mb-3">
                        <label for="last_name" class="form-label">
                            {'ms3_customer_last_name' | lexicon} <span class="text-danger">*</span>
                        </label>
                        <input type="text"
                               class="form-control {if $errors.last_name?}is-invalid{/if}"
                               id="last_name"
                               name="last_name"
                               value="{$customer.last_name}"
                               required>
                        {if $errors.last_name?}
                        <div class="invalid-feedback">{$errors.last_name}</div>
                        {/if}
                    </div>
                </div>

                {* Email with verification status *}
                <div class="mb-3">
                    <label for="email" class="form-label">
                        {'ms3_customer_email' | lexicon} <span class="text-danger">*</span>
                    </label>
                    <div class="input-group">
                        <input type="email"
                               class="form-control {if $errors.email?}is-invalid{/if}"
                               id="email"
                               name="email"
                               value="{$customer.email}"
                               required>
                        {if $email_verified}
                        <span class="input-group-text bg-success text-white">
                            {'ms3_customer_email_verified' | lexicon}
                        </span>
                        {else}
                        <button class="btn btn-outline-warning" type="button"
                                id="resend-verification-email"
                                data-customer-id="{$customer.id}">
                            {'ms3_customer_email_send_verification' | lexicon}
                        </button>
                        {/if}
                    </div>
                    {if $errors.email?}
                    <div class="invalid-feedback d-block">{$errors.email}</div>
                    {/if}
                </div>

                {* Phone *}
                <div class="mb-3">
                    <label for="phone" class="form-label">
                        {'ms3_customer_phone' | lexicon} <span class="text-danger">*</span>
                    </label>
                    <input type="tel"
                           class="form-control {if $errors.phone?}is-invalid{/if}"
                           id="phone"
                           name="phone"
                           value="{$customer.phone}"
                           required>
                    {if $errors.phone?}
                    <div class="invalid-feedback">{$errors.phone}</div>
                    {/if}
                </div>

                {* Save button *}
                <div class="d-flex justify-content-end gap-2 mt-4">
                    <button type="submit" class="btn btn-primary ms3_link">
                        {'ms3_customer_profile_save' | lexicon}
                    </button>
                </div>
            </form>
        </div>
    </div>
</div>
{/block}

Email verification

The profile shows email verification status:

  • Verified — green badge with checkmark
  • Not verified — button to send confirmation email
fenom
{if $email_verified}
    <span class="input-group-text bg-success text-white">
        {'ms3_customer_email_verified' | lexicon}
    </span>
{else}
    <button class="btn btn-outline-warning" type="button"
            id="resend-verification-email"
            data-customer-id="{$customer.id}">
        {'ms3_customer_email_send_verification' | lexicon}
    </button>
{/if}

Form handling

The form is submitted via POST with action customer/update-profile:

html
<form class="ms3_form ms3-customer-profile-form" method="post">
    <input type="hidden" name="ms3_action" value="customer/update-profile">
    <!-- form fields -->
    <button type="submit" class="btn btn-primary ms3_link">
        Save
    </button>
</form>

MiniShop3 JavaScript intercepts form submission for forms with the ms3_form class and processes it through the API.

Error display

Validation errors are passed in the {$errors} array:

fenom
<input type="text"
       class="form-control {if $errors.first_name?}is-invalid{/if}"
       name="first_name"
       value="{$customer.first_name}">
{if $errors.first_name?}
<div class="invalid-feedback">{$errors.first_name}</div>
{/if}

System settings

SettingDescription
ms3_customer_profile_page_idProfile page ID

Customization

Custom profile chunk

fenom
{'!msCustomer' | snippet : [
    'service' => 'profile',
    'tpl' => 'tpl.myProfile'
]}

Adding fields

To add extra profile fields:

  1. Add fields to the msCustomer model (via migration)
  2. Add fields to the profile chunk
  3. Handle the new fields in the API endpoint

CSS classes

ClassElement
.ms3-customer-profileProfile container
.ms3-customer-profile-formProfile form
.ms3_formMiniShop3 form (for JS)
.ms3_linkSubmit button