Skip to content

Code samples

This document shows code samples in order to help you play with the Hapify syntax like a king.

Pre-requisites

Before reading this article, we recommend that you read the documentation about Hapify syntax.

Names

Create a class for the current model

This block creates a class for the model (in pascal case) and sets the name of the primary key, in snake case.

class <<Model pascal>> {
    private primaryKey = '<<PrimaryField snake>>';
}
class <<M AA>> {
    private primaryKey = '<<P a_a>>';
}
class Place {
    private primaryKey = '_id';
}

Conditions

Include dependencies depending on field properties

In a single-model template, this block requires the MongoDb driver if the model has a relation to another one.

<<if Fields entity>>
const mongoDb = require('mongodb');
<<endif>>
<<? F tE>>
const mongoDb = require('mongodb');
<<?>>
const mongoDb = require('mongodb');

Include session validation if the action requires authentication

In a single-model template, if the action create requires at least an authenticated user, this block gets the connected user.

<<if CreateAccess gteAuth>>
const user = Session.getCurrent();
<<endif>>
<<? Ac [au>>
const user = Session.getCurrent();
<<?>>
const user = Session.getCurrent();

Test if the model is geo-located

In a single-model template, if the model has the property isGeolocated (i.e. if the model contains at least one latitude field and one longitude field), this block calls the map position picker component.

<<if Model isGeolocated>>
<app-map-position-picker [model]="<<Model camel>>"></app-map-position-picker>
<<endif>>
<<? M pGeo>>
<app-map-position-picker [model]="<<M aA>>"></app-map-position-picker>
<<?>>
<app-map-position-picker [model]="place"></app-map-position-picker>

Get relations based on cardinality

This example creates a method to fetch entities from a store, regarding the relation type: one-to-one, one-to-many or many-to-many.

class <<Model pascal>> extends BaseModel {
<<for Fields entity field>>
    get<<field pascal>>() {
    <<if field oneOne>>
        return this.<<field.model camel>>Store.findOne(this.properties.<<field camel>>);
    <<elseif field oneMany or manyMany>>
        return this.<<field.model camel>>Store.findMany(this.properties.<<field camel>>);
    <<endif>>
    }
<<endfor>>
}
class <<M AA>> extends BaseModel {
<<@ F tE f>>
    get<<f AA>>() {
    <<? f tEoo>>
        return this.<<f.m aA>>Store.findOne(this.properties.<<f aA>>);
    <<?? f tEom + tEmm>>
        return this.<<f.m aA>>Store.findMany(this.properties.<<f aA>>);
    <<?>>
    }
<<@>>
}
class User extends BaseModel {
    getAvatar() {
        return this.avatarStore.findOne(this.properties.avatar);
    }
    getBookmarks() {
        return this.placeStore.findMany(this.properties.bookmarks);
    }
}

Iterations

Populate an array with all hidden field names

In a single-model template, this block creates an array (in Javascript) that contains the hidden (hd) field names (in camel case).

const hiddenFields = [
<<for Fields hidden f>>
    '<<f camel>>',
<<endif>>
];
const hiddenFields = [
<<@ F hd f>>
    '<<f aA>>',
<<?>>
];
const hiddenFields = [
    'password',
    'token',
];

Create an array containing all possible values of an enum

In a single-model template, this block defines enums values as a arrays (in constant case):

<<for Fields enum field>>
const <<field camel>>Values = [
    <<for field.enum e>>
    '<<e constant>>',
    <<endfor>>
];
<<endfor>>
<<@ F tU f>>
const <<f aA>>Values = [
    <<@ f.e e>>
    '<<e A_A>>',
    <<@>>
];
<<@>>
const roleValues = [
    'ADMIN',
    'USER',
    'CUSTOMER',
];
const statusValues = [
    'BUSY',
    'AVAILABLE',
    'OUT_OF_OFFICE',
];

Create an index file that includes all models

In a multiple-model template, this will call all models' index files.

<<for Models m>>
require_once('./<<m kebab>>.php'),
<<endfor>>
<<@ M m>>
require_once('./<<m a-a>>.php'),
<<@>>
require_once('./user.php'),
require_once('./place.php'),
require_once('./service.php'),
require_once('./place-category.php'),

Create an index file that includes models accessible by admins only

If you want to restrict the previous loop for models that only contain admin actions:

<<for Models onlyAdmin m>>
require_once('./<<m kebab>>.php'),
<<endfor>>
<<@ M pOAd m>>
require_once('./<<m a-a>>.php'),
<<@>>
require_once('./menu.php'),
require_once('./menu-part.php'),
require_once('./menu-item.php'),
require_once('./order.php'),

Define a default value depending on data type for internal fields

In a single-model template, this block assigns a value to the field depending on its type for all internal (in) fields. If the type of the field is boolean it assigns false, if the type is string it assigns '', if the type is number it assigns 0, else it assigns NULL. This template outputs PHP.

<<for Fields internal f>>
    <<if f boolean>>
$default<<f pascal>> = false;
    <<elseif f string>>
$default<<f pascal>> = '';
    <<elseif f number>>
$default<<f pascal>> = 0;
    <<else>>
$default<<f pascal>> = NULL;
    <<endif>>
<<endfor>>
<<@ F in f>>
    <<? f tB>>
$default<<f AA>> = false;
    <<?? f tS>>
$default<<f AA>> = '';
    <<?? f tN>>
$default<<f AA>> = 0;
    <<??>>
$default<<f AA>> = NULL;
    <<?>>
<<@>>
$defaultId = '';
$defaultCreatedAt = NULL;
$defaultStock = 0;

Requires all dependencies

In a single-model template, this block requires other models pointed by entity fields. If the model has a self-dependency, it won't be included in the loop.

<<for Dependencies d>>
import {<<d pascal>>} from '../<<d kebab>>';
<<endfor>>
<<@ D d>>
import {<<d AA>>} from '../<<d a-a>>';
<<@>>
import {Restaurant} from '../restaurant';
import {User} from '../user';
import {MenuPart} from '../menu-part';
import {MenuItem} from '../menu-item';

You can also filter models by field properties. This block excludes models that contain hidden fields:

<<for Dependencies not hidden dep>>
import {<<dep pascal>>} from '../<<dep kebab>>';
<<endfor>>
<<@ D !hd d>>
import {<<d AA>>} from '../<<d a-a>>';
<<@>>
import {PlaceCategory} from '../place-category';
import {Service} from '../service';
import {User} from '../user';

Cascading deletion

In a single-model template, this block lists all models that refer to the current one and deletes them. The first iteration loops over all models that have dependency to this one. The second iteration loops over all entity relations contained in those dependent models.

Note: The ReferencedIn array contains all models that refer to the current one through entity fields. Only referring entity fields are defined in those referring models. Therefore, if you loop over refers' fields, you won't be disturbed by other fields.

<<@ ReferencedIn model>>
    <<for model.fields field>>
await db.collection('<<model pascal>>').deleteMany({ <<field snake>>: id });
    <<endfor>>
<<@>>
<<@ R m>>
    <<@ m.f f>>
await db.collection('<<m AA>>').deleteMany({ <<f a_a>>: id });
    <<@>>
<<@>>
await db.collection('Place').deleteMany({ owner: id });
await db.collection('Bookmark').deleteMany({ owner: id });
await db.collection('Message').deleteMany({ sender: id });
await db.collection('Message').deleteMany({ recipient: id });
await db.collection('Conversation').deleteMany({ participants: id });
await db.collection('Conversation').deleteMany({ closed_by: id });
await db.collection('ConversationReport').deleteMany({ complainant: id });
await db.collection('ConversationReport').deleteMany({ defendant: id });