flameart/restyii2

Yii 2 RESTful template

This package's canonical repository appears to be gone and the package has been frozen as a result.

v1.0.0 2022-04-27 20:36 UTC

This package is auto-updated.

Last update: 2023-12-26 19:03:49 UTC


README

Fast prototyping backend starter for SPA applications and mobiles. All main features for a modern app installed and configured.

Features:

  • JSON-based CRUD requests, similar to GraphQL, with infinity relations deep
    • Generate models and REST controllers for all tables in one click via Gii
    • Generate Typescript ORM imports
    • Performance optimized
  • Signup and Authentication
    • Google, Facebook and other third-party oauth-client ready
  • Roles system by default, correct work with guests
    • Customizable access rules for each field in a table per user role
  • Rate limiter: anti-bruteforce, anti-scraper and anti-ddos for specified controllers, by default installed for auth module only (but better to use nginx)
  • Trees full support via Materialized Path paradigm
  • Automatic migrations: FlameArt Advanced migration generator tool included, can generate migration for tables and data automatically

Getting started

Install steps

  1. Install to folder projectName

     composer create-project --prefer-dist --remove-vcs --stability=dev flameart/restyii2 projectName
     cd projectName
     php init --env=Development --overwrite=All
    

    For release on production just replace env to --env=Production, and use composer update instead of composer create-project

  2. Configure you DB settings in /common/config/main-local.php

  3. Init migrations

     php yii migrate --interactive=0
     php yii migrate --migrationPath=@yii/rbac/migrations --interactive=0
     php yii install/init
    

Now system works. You can setup domain for it and check: http://yourdomain/site/index

Create REST automatically

  1. Create your tables in Database
  2. Open http://yourdomain/gii/restgen (working only in dev mode)
  3. Select tables, click Preview and then Generate
    • Or you can just run yii gii/restgen --tableName=* --interactive=0 --overwrite=1 - it make all models and controllers
  4. System automatically create:
    1. Controllers in /rest/controllers/api/
    2. Configurable models for tables in /common/models/DB/, which extends classic ActiveRecord models
    3. Classic ActiveRecord models in /common/models/DB/models. No need change this, that will be rebuilded after changing columns or relations. Use only configurable models
  5. Done! Check you REST http://yourdomain/api/v1/TABLENAME/index - for select first 20 rows

Work with REST

I have javascript module with predefined methods: https://github.com/FlameArt/rest, or you can manually compound JSON-requests with next methods

Get data from DB

Typically use, get 2 specified columns for active users with sort by username and pagination: POST http://yourdomain/api/v1/user/index

{
    fields: ["username", "phone"],
    where: {"status": 'ACTIVE'},
    sort: "username",
    page: 1
}
  • If fields not set - show all fields, accessible for current user
  • If where, sort not set - without filters or sort
  • If page, per-page not set - page 1 and 20 rows per page by default

All-featured request with comments:

{
    ... Your select fields. Field "user" here have a Foreign key, so you just write all fields what you need from related table:     
    fields: {"id":"", "title":"", "user":
        {
            "name": "",
            "phone": "",
            ... you can do infinity deep if columns have foreign keys, also here an array format with just field list ...
            "country": ["code", "shortname"]
        }
    },
    
    where: {
    
        ... WHERE status=5
        "status": 5,
        
         ... Yii2 format for custom conditions, example:
        "searchText": {"LIKE", "title", "some text"},
        
         ... FullText search (match - against):
        "searchByFulltextIndex": {"FULLTEXT", "column", "some text"},

        ... and you can combine multiply AND & OR, details Yii2 format: https://www.yiiframework.com/doc/guide/2.0/en/db-query-builder#where
        
    },
    
}
Find in MySQL JSON-fields

App supports search JSON-fields in SQL with JSON_CONTAINS (equivalent OR) for each element in array (MySQL 5.7+)

{
    where: [
        json_field: [1,3,5]
    ]
}

Delete items

ACCESS_RULES can use next values for delete rule:

  • * - no rules, any user can delete any row
  • self - user can remove own rows (comparing $USER_ATTRIBUTE of current table and user id)
  • function($model, $data) - you can configure you own access checking for delete rows
  • null or delete section do not exists - user can't remove rows

Trees

Realized via fast Materialized Path. It compound all tree via 1 query. But you can use NestedTree or other paradigm.

Create migration php yii migrate/create addTreeColumns like this (items - your table):

$this->addColumn('items', 'm_tree', $this->integer()->defaultValue(null));
$this->addColumn('items', 'm_path', $this->string(500)->defaultValue(null));
$this->addColumn('items', 'm_depth', $this->integer()->defaultValue(null));
$this->addColumn('items', 'm_sort', $this->integer()->defaultValue(null));

$this->createIndex('path', 'items', ['path']);

And migrate php yii migrate.

Now, you can append next parameters to query in Create or Edit requests:

  • appendTo=0 for new tree or root item
  • appendTo=123 append new or edited item to other item with primaryKey 123 as last element
  • insertAfter=123 move (or add new) node with this primaryKey in position after other node
  • insertFirst=123 move node as first element of parent node with primaryKey 123

Delete request remove ELEMENT AND ALL CHILDS by default. For save childs for any purposes:

  • withoutChildrens for remove a node without childs (they will moved to parent of deleted node)

Read trees

For populate a tree just add to request tree param with primary key of root node like this:

{
    "tree": 123
    "where": {"status":1},
}

123 in example - is the primaryKey (id) for node, which you need to get all it childrens As you see, you can use all other params like where, fields etc. Response has additional field m_parent with primary Key of parent node for each column

Populate all tree for current user

{"tree":0}

Gii autogenerator

Auto-generate table models and typescript imports:

  • Via web interface:

  • Via command line for update ALL tables:

    • yii gii/restgen --tableName=* --interactive=0 --overwrite=1

Typescript ORM models at rest/TSImports/

Безопасность доступа к данным

В отличие от ручной настройки сущностей для каждого запроса, в данном шаблоне настраиваются ограничения на таблицы для каждого юзера, какие поля он может видеть и изменять, а так же используется и классическая схема с RBAC для описания более глобальных уровней доступа. Это позволяет продолжать прогать только фронтенд и запрашивать из него только те поля, что нужны, а система будет лишь проверять возможность доступов к этим полям. Это позволяет настроить бек только 1 раз, в чём и состоит его изначальная задумка, сохраняя уровень безопасности.

Self field param: it comparing $USER_ATTRIBUTE of model with current User ID. Default field name for User ID in DB configured to user, so if you use other field name, change $USER_ATTRIBUTE in the model. For example, User model checks with id (user can view or edit only self settings)

Signup and auth

Social

Note: signup and auth via socials working only on real domain

You need to fill hostInfo in /rest/config/main.php -> requests with your site domain

Also, you need to register your apps in services and get Client ID and Secret key, then write it in /rest/config/main.php -> authClientCollection

Small instructions how to get these keys:

Emails

Firstly, you need to configure smtp-account for email, the reason: smtp-mailing is more secure and trustable option, than native clients, which sent emails directly to spam folder, and 2nd: you can disable unsafe proc_open in function list. For security reasons services like Gmail using Temp App Passwords, create it here: https://myaccount.google.com/u/1/apppasswords

Then configure /common/config/main.php -> mailer component, just write full email (myname@gmail.com) and app password

Email design

All email templates placed at /common/mails/, Welcome template will sent after signup and verification by default (for signup via socials verification not needed)

Files upload

Created with automatic upload. It save files into storage with path and save field in DB with all path with ; delimiter. Removes old files, if needed - after replace with new files, after problems with save to DB and after delete row.

Request from client needed (edit or create):

"myFilesField": [
    {
        "name": "trollface.jpg",
        "data": "base64 encoded image"
    },
    {
        "name": "file.zip",
        "data": "base64 encoded file"
    },
]

You need rules image or file validator like this rules section:

     [['avatar'], 'image', 'extensions' => ['png', 'gif', 'jpg', ], 'maxSize' => 1024*1024, 'maxFiles' => 1]

UploadBehavior - enabled by default for each model, it finds fields in rules with image or file validators. Each field in the validator - use for automatic uploads and saves to DB. You can type your own folder for each field in fieldsFolders parameter like this:

   [
         'class' => UploadBehavior::class,
         'fieldsFolders' => [
            'avatar' => '/usersdata/avatar/',
            'uploads' => '/usersdata/uploads/'
         ]
   ]

Versioning

You can add versions in GlobalRestConfig, Rest Generator create pure controller for each version. Existing controllers will not be overwritten.

Nginx settings

You need to add /usersdata/ location to config under same uri yourdomain/usersdata. It contains users uploads, uploaded and default avatars.

Push notifications

Apple

  1. Go to Apple Developer Center -> Certificates, Identifiers & Profiles -> Certificates -> + -> Apple Push Notification service SSL (Sandbox & Production)
  2. Create CSR-key openssl req -new -newkey rsa:2048 -nodes -keyout yourname.key -out yourname.csr
  3. Upload to apple, download .cer certificate
  4. Make PEM file openssl x509 -in your_certificate.cer -inform der -out your_certificate.pem

Google (Android)

  1. Go to Firebase https://console.firebase.google.com/ and create a project
  2. Project Overview -> Project settings -> Cloud Messaging
  3. Project credentials -> Server key

FAQ

Why cookies-based sessions and not JWT-token?

Security reasons:

  • If SPA-app store token on localstorage - it`s definitely unsafe
  • If we set JWT-token into Cookies with SameSite HttpOnly, a leak of JWT secret-key let fake any request for any user. There is additional check needed: a fact that token was sended.

So, no difference between token and session: in any way we need to check it via db.

Anyway, JWT-auth included in the template and you can use it