Getting started

Getting started

Install NiangPro, choose a site type, and take a tour of what was generated.

Installation

A new project is created with composer create-project:

bash
composer create-project niangpro/niangpro my-app
cd my-app
./bin/niang serve

The created project holds your application; the framework, niangpro/framework, is a dependency installed in vendor/ and updated with composer update niangpro/framework. A project created with version 1.x was a copy of the framework: to move it to 2.0, follow UPGRADE.md in the framework repository.

./bin/niang serve runs php -S 127.0.0.1:8000 -t public — no dependency on Apache or Nginx for local development. Another host/port pair can be passed as an argument: ./bin/niang serve 0.0.0.0:8080.

composer create-project also creates .env (a copy of .env.example) with an APP_KEY unique to the project — the secret that signs password reset links and encrypts cookies (see Security).

To contribute to the framework itself (cloning the repository directly rather than creating a new project), the same installation is done with git clone + composer install + cp .env.example .env + ./bin/niang key:generate.

Choosing a site type

At the end of the installation (or of ./bin/niang new my-app, the equivalent used from inside a NiangPro project, which runs composer create-project niangpro/niangpro into a sibling folder), NiangPro asks an interactive question. It is handled by Niang\Core\Console\SiteTypePrompt and Niang\Core\Console\ProjectScaffolder, and prints exactly this (the CLI speaks French):

text
Quel type de site souhaitez-vous construire ?

  1) vitrine    Site vitrine / informationnel
  2) ecommerce  Boutique en ligne
  3) blog       Blog / magazine
  4) portfolio  Portfolio
  5) landing    Landing page one-page
  6) auth       Application avec comptes (inscription, connexion, espace membre)
  7) api        API REST (JSON, jetons, CORS, OpenAPI)
  8) saas       SaaS (organisations, membres, abonnements)
  9) minimal    Minimal — squelette de démonstration (défaut)

Votre choix [9] :

That is: “Which type of site do you want to build?” — a showcase/informational site, an online store, a blog/magazine, a portfolio, a one-page landing page, an application with user accounts (sign-up, login, member area), a JSON REST API, a SaaS with organizations and subscriptions, or the minimal demo skeleton (the default). The list comes from resources/scaffold/themes/*: every subfolder with a valid theme.json shows up automatically (see ProjectScaffolder::catalog()), with minimal always last and the default. Three ways to skip the question:

  • --type=blog on the command line (./bin/niang new my-app --type=blog);
  • the NIANG_SITE_TYPE environment variable (NIANG_SITE_TYPE=blog composer create-project niangpro/niangpro my-app --no-interaction);
  • running the command in a non-interactive context (CI, pipe, docker run without -t): the minimal skeleton is silently kept, without asking.

An explicitly requested type (--type or NIANG_SITE_TYPE) that does not exist aborts the installation with an error message listing the available types — never a silent fallback in that specific case. An installed theme does not merge with the minimal skeleton: it replaces its demo views, routes and assets (see ProjectScaffolder::install()).

Generated project structure

Your code (app/, routes/, resources/views/...) and the framework (vendor/niangpro/framework) are kept apart. With the minimal type (the default demo), a freshly created project contains:

text
my-app/
├── app/
│   ├── Controllers/       HomeController, AuthController, PostController...
│   ├── Middleware/        VerifyCsrfToken, Authenticate, ThrottleRequests...
│   ├── Models/             User, Post, Comment, Tag...
│   ├── Providers/          AppServiceProvider
│   ├── Policies/           PostPolicy
│   ├── Requests/           ContactRequest (FormRequest)
│   ├── Resources/          PostResource (JsonResource)
│   ├── Jobs/, Listeners/, Mailables/
│   └── Console/Commands/   (created by make:command, absent at first)
├── config/                 app.php, auth.php, cors.php, security.php, session.php
├── database/
│   ├── migrations/
│   └── seeders/            DatabaseSeeder
├── public/
│   ├── index.php            HTTP entry point
│   ├── favicon.svg, logo.svg
│   └── css/, js/
├── resources/views/         native PHP views (.php)
├── routes/web.php
├── storage/
│   ├── database.sqlite
│   ├── framework/           cache (routes, config)
│   └── logs/
├── tests/
│   ├── Feature/, Security/
│   └── bootstrap.php
├── bin/niang                 CLI
├── vendor/niangpro/framework/  the framework (composer update niangpro/framework)
├── composer.json
└── .env

The framework stays readable in vendor/niangpro/framework/packages/ (14 packages: core, http, database, auth...), but is not edited: an update would overwrite it. To change a behavior, use the extension points, a Service Provider or a middleware. All the work happens in app/, routes/, resources/views/ and database/.

Running the development server

bash
./bin/niang serve

Prints NiangPro démarre sur http://127.0.0.1:8000 (“NiangPro starting on…”) then stays in the foreground (Ctrl+C to stop). It is a plain call to passthru('php -S ...'): no background process, no server configuration to write for local development.

First tour: routes and controller

public/index.php is the only HTTP entry point. It creates the Application, loads routes/web.php, and starts handling the request:

public/index.php

require dirname(__DIR__) . '/vendor/autoload.php';

use Niang\Core\Application;

$app = new Application(dirname(__DIR__));

$app->loadRoutes(dirname(__DIR__) . '/routes/web.php');

$app->run();

routes/web.php receives a ready-to-use $router variable:

routes/web.php (excerpt)
use App\Controllers\HomeController;

$router->get('/', [HomeController::class, 'index']);
$router->get('/hello/{name}', [HomeController::class, 'hello']);
$router->post('/echo', [HomeController::class, 'echoBody']);

The matching controller extends Niang\Core\Controller:

app/Controllers/HomeController.php

namespace App\Controllers;

use Niang\Core\Controller;
use Niang\Core\Http\Request;
use Niang\Core\Http\Response;

class HomeController extends Controller
{
    public function index(): Response
    {
        return $this->view('home', [
            'title' => 'Bienvenue sur NiangPro',
            'framework' => 'NiangPro',
        ]);
    }

    public function hello(string $name): Response
    {
        return $this->json([
            'message' => "Bonjour, $name !",
        ]);
    }
}

Note hello(string $name): $name is never declared anywhere as a container dependency; it is the name of the {name} route parameter, resolved directly through reflection — see the Container for the details of that resolution.

What next?

  • Core concepts — the request lifecycle, the Container, Service Providers.
  • Routing — parameters, constraints, groups, domains, resource routes.
⏱ 7.96 ms 🗄 0 requête(s) SQL 🧠 4.00 MB ↩ 200