PHP code example of neoan.io / core

1. Go to this page and download the library: Download neoan.io/core library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

neoan.io / core example snippets


// A MODEL

...
class User extends Model 
{
    
    #[IsPrimary]
    public readonly $id;
    
    public ?string $name = null;
    
    #[IsUnique]
    public string $email;
    
    public string $job = 'employee';
    
    #[Transform(Hash::class)]
    public string $password;
    
    use TimeStamps;
    use Setter;
}
 
// A CONTROLLER

...
$user = new User([
    'email'=> '[email protected]', 
    'name' => 'Someone',
    'password' => '123123'
]);
// reconsider name?
$user->name = 'Adam';
$user->store();

...
// or e.g. when updating a password

$user = User::retrieveOne([
            'email' => '[email protected]'
        ]);

// Don't worry! Hashing for this property 
// is always ensured by the model
[ 
    'newPassword' => $user->password 
] = Request::getInputs();
$user->store();



use Neoan\NeoanApp;
use Neoan\src\Routing\Route;

lo-world')
    ->inject(['msg' => 'Hello World']);

$app->run();

 
#!/usr/bin/env php

// the first line is necessary if we don't use the extension ".php"!
// this file load our cli capabilities and is exposed to
// allow advanced users to integrate own commands (based on symfony console)

use App\Config\Config;
use Neoan\Cli\Application;
use Neoan\NeoanApp;


 
use App\Config\Setup;
use App\Routes\HtmlRoutes;
use Neoan\NeoanApp;

 this very script runs

$app =  new NeoanApp($srcPath, $publicPath);
new Setup();
new HtmlRoutes();
$app->run();

 
namespace App\Routes;


class HtmlRoutes {
    function __construct()
    {
        Routes::get('/')->view('home.html');
    }
}

 
namespace App\Config;

use Neoan\Database\Database;
use NeoanIo\MarketPlace\DatabaseAdapter;
use Neoan\Helper\Env;
use Neoan\Response\Response;
use Neoan\Render\Renderer;

class Setup {
    function __construct()
    {
        // Database setup
        $dbClient = [
            'host' => Env::get('DB_HOST', 'localhost'),
            'name' => Env::get('DB_NAME', 'neoan_io'),
            'port' => Env::get('DB_PORT', 3306),
            'user' => Env::get('DB_USER', 'root'),
            'password' => Env::get('DB_PASSWORD', ''),
            'charset' => Env::get('DB_CHARSET', 'utf8mb4'),
            'casing' => Env::get('DB_CASING', 'camel'),
            'assumes_uuid' => Env::get('DB_UUID', false)
        ];
        Database::connect(new DatabaseAdapter($dbClient));
        
        // Defaults
        Response::setDefaultOutput(ResponseOutput::HTML);
        Renderer::setTemplatePath('src/Views');
    
    }
}

 
use Neoan\Routing\Route;

Route::request(string $httpMethod, string $endpoint, Routable ...$classes);
 
use Neoan\Routing\Route;

Route::get(string $endpoint, Routable ...$classes);
 
use Neoan\Routing\Route;

Route::get('/users/:id')
    ...
 
namespace App\Controllers;

use Neoan\Routing\Routable;

class Controller implements Routable
{

    public function __invoke(array $provided): array
    {
        return ["msg" => "Hello World"];
    }
}
 
use Neoan\Routing\Route;

Route::get('/', App\Controllers\Controller::class)
 
namespace App\Middleware;

use Neoan\Errors\Unauthorized;
use Neoan\Routing\Routable;
use Neoan3\Apps\Stateless;

class NeedsAuth implements Routable
{
    public function __invoke(array $provided = []): array
    {
        try{
            return ['auth' => Stateless::validate()];
        } catch (\Exception $e) {
            new Unauthorized();
        }
    }
}
 
use Neoan\Routing\Route;
use App\Middleware\NeedsAuth;
use App\Controllers\Controller;

Route::get('/', NeedsAuth::class, Controller::class)
 
namespace App\Controllers;

use Neoan\Routing\Routable;

class Controller implements Routable
{

    public function __invoke(array $provided): array
    {
        ['auth' => $auth] = $provided;
        // better not do that?
        return ["token-payload" => $auth];
    }
}
 
use Neoan\Response\Response;
use Neoan\Enums\ResponseOutput;
Response::setDefaultOutput(ResponseOutput::HTML)
 
use Neoan\Routing\Route;
use Neoan\Response\Response;
use App\Controllers\Controller;

Route::get('/', Controller::class)->response([Response::class,'html']);
// or whatever handler you want:
Route::get('/my-handler', Controller::class)->response([App\Own\MyResponseHandler::class,'answerMethod'])
 
use Neoan\Routing\Route;
use App\Controllers\Controller;

Route::get('/', Controller::class)->inject(['title'=>'my_app']);
 
use Neoan\Render\Renderer;

Renderer::setTemplatePath('src/Views');
 
use Neoan\Routing\Route;

Route::get('/')
    ->response([Response::class,'html'])
    ->inject(['user' => ['firstName' => 'Sam']])
    ->view('/home.html');
 
// e.g. in idex.php
// ...
$app = new NeoanApp( dirname(__DIR__), __DIR__, dirname(__DIR__));

// invoke using the namespace of whereever your routables are located
$app->invoke(new Neoan\Routing\AttributeRouting('Controller'));

 
// e.g. Controller\WebRoute.php
namespace Controller;

#[Web('/','/test.html')]
class WebRoute implements Neoan\Routing\Routable
{
    public function __invoke(array $provided): array
    {
        return ["msg" => "Hello World"];
    }
}


Route::get('/api/users/:id', UserShowController::class);
Route::post('/api/user', UserCreateController::class);
 
// call: GET:/api/users/1?some=value
...
public function __invoke(array $provided): array
    {
        return [
            'queryValues' => Request::getQueries(), // outputs ['some' => 'value']
            'userId' => Request::getParameter('id') // outputs "1"
        ];
    }
...
 
// call: POST:/api/user payload: {"userName":"Tobi"}
...
public function __invoke(array $provided): array
    {
        ['userName' => $userName] = Request::getInputs();
        return [
            'user' => $userName // outputs "Tobi"
        ];
    }
...
 
use Neoan\Render\Renderer;

Renderer::setTemplatePath(string $path);
 
use Neoan\Render\Renderer;

Renderer::setTemplatePath('src/Views');
 
use Neoan\Render\Renderer;

Renderer::setHtmlSkeleton(string $templatePath, string $routePlacement, array $renderVariables)
 
use Neoan\Render\Renderer;
use Neoan\Store\Store;

Renderer::setHtmlSkeleton('src/Views/main.html','routePlacement',[
    'title' => Store::dynamic('title'), // 'title' isn't set at this point, so we use the dynamic store
    'webPath' => $app->webPath          // neoan instance relative webPath in case we need it
])
 
use Neoan\Routing\Route;
use Neoan\Response\Response;
use Neoan\Enums\ResponseOutput;
use App\YouClass;

Response::setDefaultOutput(ResponseOutput::HTML);
Route::get('/test/:you', YouClass::class)->view('/you.html');
 
use Neoan\Store\Store;
use Neoan\Routing\Routable;
use Neoan\Request\Request;

class YouClass implements Routable{

    public function __invoke(Injections $provided): array
    {
        Store::write('title','you-route');  // write to dynamic store
        return Request::getParams();        // we know this 
 
 ...
 return [
    'deep' => [
        'key' => 'one'
    ],
    'iterateMe' => [
        ['name' => 'Sam'],
        ['name' => 'Adam']
    ]
 ];
 ...
 
use Neoan\Event\Event;

Event::on('log', function($event){
    $data = [
        'time' => time(),
        'event' => $event
    ];
    file_put_contents(dirname(__DIR__,2) . '/log.txt', json_encode($data), FILE_APPEND);
});

...
// somewhere else
try{
    ...code
} catch(\Exception $e){
    Event::dispatch('log', $e->getMessage());
}

 
use Neoan\Event\Event;
use Neoan\Event\Listenable;
use Neoan\Event\EventNotification;

class AnyClass implements Listenable
{
    private EventNotification $notifier;
    function __construct()
    {
        $this->notifier = Event::makeListenable($this);
    }
    function doSomething(string $value)
    {
        ...
        $this->notifier->inform($value);
    }
}
 
use Neoan\Store\Store;
$totalRuntime = Store::dynamic('totalRuntime');
$start = time();
for($i = 0; $i <2; $i++){
    echo $totalRuntime; // first iteration: null, second iteration: ~ 1
    sleep(1);
    Store::write('totalRuntime', time() - $start);
}
echo $totalRuntime; // ~ 2
 
namespace App\Models;

use Neoan\Model\Model;
use Neoan\Helper\DateHelper;
use Neoan\Model\Attributes\Initialize;
use Neoan\Model\Attributes\IsPrimaryKey;
use Neoan\Model\Attributes\IsUnique;
use Neoan\Model\Attributes\Ignore;
use Neoan\Model\Attributes\Type;
use Neoan\Model\Collection;
use Neoan\Model\Traits\TimeStamps;

class MovieModel extends Model {
    // primary keys can either be UUIDS or auto-incremented integers
    // as our database setup refused the assumption of UUIDS, integers it is! 
    // every model needs a primary key, which is indicated by the attribute "IsPrimaryKey"
    
    #[IsPrimaryKey]
    public int $id;
    
    // Can there be two movies with the same name? Let's decide no:
    // The "IsUnique" attribute let's the auto-migration know that we are serious about this decision.
    
    #[IsUnique]
    public string $name;
    
    // Let's go crazy: What if wanted a type that cannot be inferred as it isn't built in?
    // We are going to need to worry about two things: 
    // First, the database type shouldn't default to string (or varchar, in our case), 
    // so we define it using the "Type" attribute
    // Additionally, we would like our model to assume the current date when a model is created,
    // so we initialize a Datehelper instance on creation.
    
    #[
        Type('date',null),
        Initialize(new DateHelper())
    ]
    public string $releaseDate; 
    
    // Just to lighten up the attribute-overload, let's create a regular field
    // Since it has the type string it will default to a (short-)string data type (e.g. varchar(255)
    
    public string $studio;
    
    // What about relations?
    // there is more than one review for a given movie, so we attach ReviewModel instances in a
    // collection (see Collections) to the property $reviews based on the ReviewModel's foreign key
    // "movieId" which points to our primary key "id"
    
    #[HasMany(ReviewModel::class, ['movieId' => 'id'])]
    public Collection $reviews;
    
    // I don't know what I need it for, but the following property is ignored by database transactions
    // and only serves for us to store values.
    
    #[Ignore]
    public string $aProperty = 'new';
    
    // Traits can be useful to fight repetition. This packaged trait delivers us the properties
    // - createdAt (a timestamp filled at creation of the Model)
    // - updatedAt (a timestamp that is filled whenever a Model is stored to the database) and
    // - deletedAt (a timestamp allowing soft deletion)
    use TimeStamps;
}    
 
namespace App\Models;

Neoan\Model\Traits\Setter;
Neoan\Model\Model;
use Neoan\Model\Attributes\IsPrimaryKey;
use Neoan\Model\Attributes\IsForeignKey;
use Neoan\Model\Traits\TimeStamps;

class ReviewModel extends Model{
    
    // Young devs in your team?
    // It's probably smart to set the primary key to "readonly" to protect your padawans
    // from stupid ideas. However, this fault
    
    #[Type('MEDIUMTEXT', null, 'Awesome')]
    public string $content;
    
    // Remember our model "Movie"? 
    // While we don't need to declare this as foreign key,
    // we might want to speed up database queries once our cinema bursts with visitors
    
    #[IsForeignKey]
    public int $movieId;
    
    use TimeStamps;
    
    // Want to make your despise for critics known to whoever has to write raw
    // queries? Name your table however you like instead of being base on the model name.
    
    const tableName = 'ticks';
    
}
 
...
// either initialte with an assoc array
$movie = new MovieModel([
    'name' => 'The Matrix'  
]);

// or set the individual property
$movie->studio = 'Warner Bros.'; 

// If you are ready to store the movie to the database (and rehydrate), run store()
$movie->store();

// This will now 
 
...
$movie = new MovieModel(Request::getInputs());
try{
    $movie->store();
} catch (\Exception $e) {
    // 
 
...
// The following is NOT recommended in our scenario!
// This is only to show you the possibilities

$movie = new MovieModel();

// will return Neoan\Enums\TransactionType::INSERT
$mode = $move->getTransactionMode(); 
$movie->setTransactionType(TransactionType::UPDATE); 
 
// sometimes I know the primary id ...
$matrix = MovieModel::get(1); 

// ... but often I lookup based on what I know
$matrix = MovieModel::retrieveOne([
            'name' => 'The Matrix'
          ]); 

// ... maybe I even want to create it if it doesn't exist
$matix = MovieModel::retrieveOneOrCreate([
            'name' => 'The Matrix'
          ]); 

// Let's fix the name
$matix->studio = 'Warner Bros. Pictures'

// Then simply store again
$matrix->store();
 
...
// First, lets retrieve multiple records
// Instead of "retrieveOne" we will use "retrieve"
// Additionally, we account for soft deleted records and 
// want to ignore them by adding a condition to our retrieval 
$allMovies = MovieModel::retrieve(['deletedAt' => null]);

// Collections are iterable
foreach($allMovies as $movie){
    ...
}

// However, it would be a shame if our modern IDE couldn't 
// help us with existing properties. So let's use "each" instead
$allMovies->each(function(MovieModel $movie, int $iteration){
    ...
});

// Did you do something to all the records there?
// Let's save all selected movies at once
$allMovies->store();

// While you can return collections directly, 
// you might need to convert them to an array
$flat = $allMovies->toArray();

// Didn't find what you are looking for?
// Just add to the existing collection
$allMovies->add(new MovieModel(['name' => 'Alien']))
 
...
$currentPage = 1;
$pageSize = 25;

return MovieModel::paginate($currentPage, $pageSize)

    // are there conditions/filters to this list?
    ->where(['studio' => 'Warner Bros. Pictures'])
    
    // controlling the sort
    ->descending('year')
    
    // finally, execute the pagination request
    ->get();

 
[
    'page' => 1,    // current page
    'total' => 50,      // total hits
    'pageSize' => 30,       // number of results per page
    'pages' => 2,       // total number of resulting pages
    'collection' => `{Collection}`      // result as Collection
]
 
#!/usr/bin/env php

...
$console = new Application($app);
$console->add(new MyOwnCommand($app));
...

project
+-- public
|   +-- index.php
+-- src
|    +-- Attributes
|    +-- Cli
|    +-- Config
|    |    +-- Setup.php
|    +-- Controllers
|    +-- Middleware
|    +-- Models
|    +-- Routes
|    |   +-- HtmlRoutes.php
|    +-- Views
|        +-- main.html
|        +-- home.html
+-- vendor
+-- cli
+-- composer.json
shell
php cli migrate:models sqlite 
shell 
php cli list