Create Your First Project

Installation

  1. Make sure Composer is installed on your server. If it's not, please install it.
  2. Create a project folder and navigate to it.
  3. Run the command composer initand answer the questions to create your project. For this example, I used the name festi-demo.
  4. Replace your composer.json with the following:
    {
    "name": "your_company/project_name",
    "description": "Description",
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.festi.io/"
        }
    ],
    "require": {
        "festi-team/festi-framework-core": "dev-master",
        "festi-team/festi-framework-database": "dev-master",
        "festi-team/festi-framework-theme": "dev-master",
        "festi-team/festi-framework-cli": "dev-master",
        "festi-team/festi-framework-di": "dev-master",
        "php": ">=8.0"
    },
    "minimum-stability": "dev",
    "autoload": {
        "psr-4": {
            "": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "": [
                "tests"
            ]
        }
    },
    "require-dev": {
        "phpunit/phpunit": "9.*",
        "phpunit/php-code-coverage": "9.*",
        "phan/phan": "4.x",
        "squizlabs/php_codesniffer": "3.*"
    }
    }
    
  5. Run the command composer install.
  6. Create a database if you haven't already.
  7. Now, use the ./vendor/bin/festi-install tool to install the project.

Festi Install Tool

./vendor/bin/festi-install was created to make the developer's life easier.

When you run the tool, the first question will be: "Which installation option do you want?". You can choose one of several options:

  • dashboard - Option when you need to create an admin panel or dashboard with ACL support.
  • site - Website or portal
  • api - RESTful API
  • rpc - RPC API
  • async - Asynchronous service for IoT, Game, Data Streaming, etc.

Choose the option you need; for this example, we'll choose dashboard. Next, you'll need to provide database access, and generally, all the following installation steps should not cause any problems. Each installation type also comes with demo content to structure the project right after installation.

More information about the installation tool can be found in the Festi CLI section.

After installation, configure the server to point to the src/dashboard/ folder as the project's root.

It's often convenient to use symbolic links (symlinks), especially if you're using control panels like CentOS Panel, Plesk, etc. For example: ln -s festi-demo/src/dashboard/ public_html.

You should see an authentication form:

Authentication Form

For proper operation, make sure that your php.ini file has the following settings:

open_short_tag = Off

Creating the First Plugin

Let's create a section for managing company expenses together. To do this, we need to create a new plugin called Expenses using the ./vendor/bin/festi-plugin utility:

cd src/dashboard/
../../vendor/bin/festi-plugin

During creation, you need to provide the following parameters:

What do you want to create? [create, url, dgs]: create
Enter Plugin Name: [required]: Expenses

Now, let's take a look at the structure of the plugin:

ls -l plugins/Expenses/
ExpensesObject.php
ExpensesPlugin.php
init.php
tblDefs
templates
  • ExpensesObject.php - This is a class that implements DataAccessObject for working with data in the database.
  • ExpensesPlugin.php - This is a class that describes the business logic of the plugin and serves as a facade.
  • init.php - This file is called at runtime for each request to the server.
  • tblDefs - This is a folder for schemas that describe DataGridStore.
  • templates - This is a folder for templates.

More information about the plugin structure can be found in the Plugins section.

Creating the First DGS (DataGridStore)

DGS (DataGridStore) is a special component for displaying data in the form of a table. To describe a DGS, we need to define a schema in one of the available formats: JSON, XML, or an array. We can create a schema using the utility ./vendor/bin/festi-dgs or ./vendor/bin/festi-plugin. Let's create a schema for displaying expenses using the ./vendor/bin/festi-plugin utility:

  1. First, let's create an expenses table in the database:
    CREATE TABLE `expenses` (
    `id` int(10) UNSIGNED NOT NULL,
    `cdate` date NOT NULL,
    `id_user` int(10) UNSIGNED NOT NULL,
    `amount` int(11) NOT NULL,
    `note` text NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
    
    
    ALTER TABLE `expenses`
    ADD PRIMARY KEY (`id`),
    ADD KEY `id_user` (`id_user`);
    
    
    ALTER TABLE `expenses`
    MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
    
    
    ALTER TABLE `expenses`
    ADD CONSTRAINT `expenses_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `users` (`id`) ON UPDATE CASCADE;
  2. Next, run the ./vendor/bin/festi-plugin utility and select the dgs option:
    What do you want to create? [create, url, dgs]: dgs
    Enter Table Name: [required]: expenses
    Enter Plugin Name: [required]: Expenses
    Enter RegExp URL expression (ex: ~^/company/([0-9]+)/reports/$~): [required]: ~^/expenses/$~

After creating the DGS, the utility will generate routing rules and a method that will be called when navigating to /expenses/.

class ExpensesPlugin extends DisplayPlugin
{
    /**
     * @urlRule ~^/expenses/$~
     * @section none
     * @area backend
     * @userType user
     */
    public function onDisplayExpenses(Response &$response)
    {
        $store = $this->createStoreInstance("expenses");

        $store->onRequest($response);

        return true;
    } // end onDisplayExpenses
}

Now, let's open the page /expenses/ in your browser:

Expenses Page

We have automatically created a DGS based on the table created in the database. Now, let's take a look at how the schema looks (/plugins/Expenses/tblDefs/expenses.xml):

<?xml version="1.0" encoding="UTF-8"?>
<table charset="UTF-8"
       name="expenses"
       primaryKey="id"
       defaultOrderField="id"
       defaultOrderDirection="DESC"
       rowsForPage="50">
    <fields>
        <field type="readonly"
               name="id"
               required="true"
               sorting="true"
               caption="<?php echo __('Id'); ?>"
               filter="text" />

        <field type="datetime"
               name="cdate"
               html5="true"
               required="true"
               format="%m/%d/%Y"
               caption="<?php echo __('Cdate'); ?>"
               filter="text" />

        <field type="foreignKey"
               name="id_user"
               required="true"
               foreignTable="users"
               foreignKeyField="id"
               foreignValueField="id"
               caption="<?php echo __('Id User'); ?>"
               filter="select" />

        <field type="number"
               name="amount"
               required="true"
               caption="<?php echo __('Amount'); ?>"
               filter="text" />

        <field type="text"
               name="note"
               required="true"
               caption="<?php echo __('Note'); ?>"
               filter="text" />
    </fields>
    <actions>
        <action type="list"
                caption="<?php echo __('Expenses'); ?>" />
        <action type="insert"
                caption="<?php echo __('Add'); ?>" />
        <action type="edit"
                caption="<?php echo __('Edit'); ?>" />
        <action type="info"
                caption="<?php echo __('Info'); ?>" />
        <action type="remove"
                caption="<?php echo __('Delete'); ?>" />
    </actions>
</table>
In general, the rules for describing a schema are quite simple. For more details, you can read the documentation. Let's go through the key points:

  • table - the root element that describes a table, which can represent not only a database table but also an aggregated representation, for example, an API of some service.
  • fields - describes the fields of the table that will be displayed in the table.
  • actions - describes the actions that can be performed on records.
  • field - describes a table field.
  • action - describes an action.

You can create your own fields and actions, and you can also describe a large number of additional attributes. DGS has a wide range of events that allow you to add any business logic and modify any element.

Let's make some changes to the DGS schema to make it more organized:

<?xml version="1.0" encoding="UTF-8"?>
<table charset="UTF-8"
            name="expenses"
            primaryKey="id"
            defaultOrderField="id"
            defaultOrderDirection="DESC"
            rowsForPage="50">
    <fields>
        <field type="readonly"
            name="id"
            sorting="true"
            caption="<?php echo __('ID'); ?>"
            width="5%"
            filter="text" />

        <field type="datetime"
            name="cdate"
            html5="true"
            required="true"
            onlyList="true"
            format="%m/%d/%Y"
            caption="<?php echo __('Create Date'); ?>"
            filter="text" />

        <field type="foreignKey"
            name="id_user"
            required="true"
            foreignTable="users"
            foreignKeyField="id"
            foreignValueField="login"
            caption="<?php echo __('User'); ?>"
            filter="text" />

        <field type="price"
            name="amount"
            required="true"
            caption="<?php echo __('Amount'); ?>" />

        <field type="textarea"
            name="note"
            width="30%"
            caption="<?php echo __('Note'); ?>" />
    </fields>

    <externalValues>
        <value field="cdate"><?php echo date('Y-m-d H:i:s'); ?></value>
    </externalValues>

    <actions>
        <action type="list"
            caption="<?php echo __('Expenses'); ?>" />
        <action type="insert"
            caption="<?php echo __('Add'); ?>" />
        <action type="edit"
            caption="<?php echo __('Edit'); ?>" />
        <action type="remove"
            caption="<?php echo __('Delete'); ?>" />
    </actions>
</table>

In this section, we've made the following changes:

  • We've improved the appearance of the fields for a more organized look.
  • We've added the externalValues field to set default values for the cdate field.

Now, our add-record form looks like this:

Add Record Form

When adding a new record, the creation date is automatically filled in:

Add Record Form with Date

More information about the DGS can be found in the DGS section.

Result

We've created a section for managing company expenses.

The project structure can be customized based on your needs. However, below is an example of a standard project structure:

- core
- libs
- logs
- src
    - dashboard
        - core -> ../../core
        - plugins
        - themes
        - static
            - images
                - logo.png
            - css
                - styles.css
        config.php
        common.php
        index.php
        local.php
- tests
- docs
- vendor
- README.md

If you use Composer, the core folder is not required since dependencies are managed automatically.