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:
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):
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=blogon the command line (./bin/niang new my-app --type=blog);- the
NIANG_SITE_TYPEenvironment 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 runwithout-t): theminimalskeleton 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:
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
./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:
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:
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:
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.