atomdev/odinfmk

1.7.6 2019-10-06 13:39 UTC

README

Ødin 是一个简单、实用、高效的 PHP 框架。旨在做得多,写得更少。如果您还没有许可证,请联系我们 contato@atomsi.com.br

文档

安装

使用 Composer 安装 Ødin。

composer require atomdev/odinfmk

开始一个项目

首先,您必须在 Composer 的 autoload 文件(vendor/autoload.php)中定义常量 ODIN_SERIALODIN_ROOT

    define("ODIN_SERIAL", "SUA_SERIAL");
    define("ODIN_ROOT", "/diretorio/raiz/do/projeto");

然后,在项目根目录下,您需要定义项目的基本结构。

config/
   |-- .env
http/
    controllers/
       |-- Aqui ficarão seus Controllers
    middlewares/
       |-- Aqui ficarão seus Middlewares
database/
    models/
       |-- Aqui ficarão seus Models
views/
   |-- Aqui ficarão suas views
utils/
   |-- Aqui ficarão suas classes de utilidades e seus Helpers
.htaccess

请注意,上述结构只是标准的基本模板。您可以根据需要创建自己的自定义文件夹。

定义项目配置

config 文件夹中,您需要创建一个名为 .env 的文件。这将作为项目的配置文件。其中将定义环境、数据库、目录和项目访问URL。以下是一个示例模型:

ENVIRONMENT     = dev
DRIVER          = mysql
SOURCE_DIR      = src/
HTTP_ROOT	= https://:8080
HTTP_ROOT_FILES = https://:8080/assets/ 

您可以使用两个环境:开发和生产。使用常量 ODIN_ENV 一起使用 ODIN_ROOTODIN_PATH,当设置为 dev 时,需要在配置文件夹中创建一个名为 dev.env 的文件,其中包含开发指令,而 .env 将成为生产指令。

定义应用程序的路由

在定义路由之前,您需要配置项目根目录中的 .htaccess 文件。打开文件后,您将看到以下结构:

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [QSA,L]

ErrorDocument 403 https:///pagina/de/erro/403
Options All -Indexes

您可以为错误 403 定义一个处理页面,这些错误是在用户尝试访问受限制的文件夹时抛出的。默认情况下,所有应用程序文件夹都通过 HTTP 禁止访问。

配置 .htaccess 后,您可以在 index.php 文件中定义您的路由,如下例所示。

    <?php
    //Incluindo o autoload do Composer
    require_once(dirname(dirname(__FILE__)) . "/vendor/autoload.php");

	//Utilizando as classes Config e Routes da Framework
	use Odin\utils\Config;
	use Odin\routes\Routes;
	
	Config::init("projeto"); //nome da pasta do seu projeto
	Routes::init();

	//Rota GET
	Routes::get("/", function(){
	    echo "Primeiro projeto com a Ødin Framework!";
	})->name("home");
	//Rota POST
	Routes::post("/", "Projeto\http\controllers\Classe:metodo");
	//Rota PUT
	Routes::put("/", "Projeto\http\controllers\Classe:metodo");
	//Rota DELETE
	Routes::delete("/", "Projeto\http\controllers\Classe:metodo");

	//Definindo um grupo de rotas
	$instance = Routes::getInstance();
	Routes::group("/grupo", function() use ($instance){
	    Routes::get("/", "Projeto\http\controller\sClasse:metodo");
	});
	
	Routes::run();

Ødin 支持 4 种 HTTP 请求类型,包括 GET、POST、PUT 和 DELETE。这为您创建 RESTful API 提供了极大的便利性和实用性。

控制器

控制器是 Callable,它们被传递到路由中,以便在访问时执行某些操作。您可以通过两种方式创建一个用于特定路由的控制器:

功能方式

    Routes::get("/teste", function(){
        echo "Olá, Mundo!";
    })

面向对象方式

Routes::get("/teste", "App\http\controllers\Teste:olaMundo");

对于面向对象的方式,您需要在 controllers 文件夹中创建一个类,如下例所示。

<?php
namespace App\http\controllers; //O namespace master é o de sua preferencia

use Odin\http\controller\Controller;

class Teste extends Controller
{
    public function olaMundo()
    {
        echo "Olá, Mundo";
    }
}

在控制器中恢复动态路由的值

Ødin 也支持动态路由,即包含变量结构的路由。以下是一个示例。

Routes::get("/filme/:categoria", "App\http\controllers\Filmes:listarPelaCategoria");

要定义路由中的动态部分,您应使用 /:nomeDaVariavel。以下是一些此类路由的请求示例:

GET https:///projeto/filme/terror
GET https:///projeto/filme/acao
GET https:///projeto/filme/romance

要恢复传递到路由中的值并将其用于应用程序,只需在控制器方法中添加一个参数即可。

class Filmes extends Controller
    {
        public function listarPelaCategoria($categoria)
        {
            echo $categoria;
        }
    }

您也可以使用正则表达式定义传递给变量的模式,如下例所示。

Routes::get("/filme/:id", "App\http\controllers\Filmes:selecionarPeloId", ["id" => "[\d]{1,8}"]);

正则表达式将强制值遵循该模式,否则将抛出错误。

路由重定向

您还可以使用路由名称进行重定向,如下例所示。

Routes::get("/login", "App\http\controllers\Login:view")->name("loginPage");

要进行重定向,只需在上下文中使用 router 属性即可。

$this->router->redirectTo("loginPage");

使用视图

与任何 MVC 应用程序一样,您可以使用视图来渲染视觉元素,如表单、列表等。在 Ødin 中,视图是从控制器中渲染的,具体是在访问路由时调用的方法中。

Config::init("projeto");
Routes::init();

Routes::get("/login", "App\http\controllers\Login:view");

控制器将在 projeto/source/views 文件夹中查找视图。

要渲染视图,只需遵循以下模型。

use Odin\view\View;

class Login extends Controller
{
    use View;

    public function view()
    {
        View::render($this, "login_page.php");
    }
 }

View::render() 的参数

下表显示了可以传递给 render 方法的参数。值默认为空的参数表示必须包含一个值。

定义默认头部和尾部

您可以将文件定义为头部和尾部来存储全局上下文信息,这些文件应位于 views 文件夹中。例如,HTML 页面的 <head> 和所有 CSS 调用,或页面末尾的 <script>。以下是一个示例。

views/header.php

<!DOCTYPE html>
<html>
    <head>
        <title>Projeto</title>
        <meta charset="utf-8"/>
        <link rel="stylesheet" type="text/css" href="./css/bootstrap.css"/>
        <link rel="stylesheet" type="text/css" href="./css/style.css"/>
    </head>
    <body>

views/footer.php

        <script src="./js/jquery.js"></script>
        <script src="./js/bootstrap.js"></script>
    </body>
</html>

index.php

Config::init("projeto");
Routes::init();

Routes::setHF("header.php", "footer.php");

Routes::get("/login", "App\http\controllers\Login:view");

如果您有一些页面需要加载特定的头部和尾部,您可以在控制器中阻止加载默认文件,如下例所示。

public function view()
{
    View::render($this, "login_page.php", [], false);
}

从控制器传递值到视图

您可以将控制器中的数据共享给视图,例如,用于制作电影列表的数据。

public function listarFilmes()
{
    View::render($this, "listar_filmes.php", [
        "itens" => ["Filme 1", "Filme 2", "Filme 3"]
    ]);
}

在视图

<div class="container">
    <table class="table table-hover">
        <thead>
            <tr>
                <th>Chave</th>
                <th>Filme</th>
            </tr>
        </thead>
        <tbody>
            <?php
                foreach($itens as $i => $item)
                {
	                echo "<tr>";
	                echo "<td>{$i}</td>";
	                echo "<td>{$item}</td>";
	                echo "</tr>";
                }
            ?>
        </tbody>
    </table>
</div>

使用相同的属性,您可以检索您在视图中定义的任何路由的 URL,从而简化页面间的导航。但为此,您需要将 router 对象作为变量传递给视图。

public function view()
{
    View::render($this, "login_page.php", [
        "router" => $this->router
    ]);
}

在视图

<a href="<?= $router->pathFor("loginPage") ?>">Login Page</a>

如果您的路由是动态的并且需要传递一个值,您可以在 pathFor() 方法的第二个参数中传递引用。

<a href="<?= $router->pathFor("listarFilmes", ["categoria" => "terror"]) ?>">Login Page</a>

全局作用域对象(依赖项)

如果您的视图需要访问某个类 utils 或 helper 的某个对象,您可以在 index.php 文件中定义全局作用域对象,如下例所示。

use Odin\Globals;

Globals::set([
    new Helper(),
    new Dependencia()
]);

在视图,您可以通过以下方式访问这些作为依赖项传递的对象

<div>
    <?= $helper->someMethod() ?>
    <?= $dependencia->algumaCoisa() ?>
</div>

中间件

Ødin 还提供了对中间件的支持,方式非常实用和简单,如下例所示。

namespace App\http\middlewares;

use Odin\http\middleware\IMiddleware;
use Odin\utils\superglobals\Session;

class Auth implements IMiddleware
{
    public function handle($request, $response, $next)
    {
        if(Session::exists("_token"))
        {
            return $next($request, $response);
        }
        else
        {
            die("Você precisa fazer login para acessar esta página");
        }
    }
}

index.php 中,您通过 add() 方法定义中间件列表。

Routes::add(["home"], [
    new Auth()
]);

Routes::get("/home", "App\http\controllers\Home:landing")->name("home");

您可以将中间件添加到路由、名称和组。要添加到所有路由的中间件,请使用 ["*"]

模型

CRUD

在开始使用项目中的模型之前,请确保在 config/.env 中定义了 DRIVER 指令,并且如果在该文件夹中有一个包含连接数据的文件,则存在该文件。例如:config/mysql.envconfig/pgsql.env

DRIVER          = mysql
HOST            = 127.0.0.1
USERNAME        = root
PASSWORD        = root
PORT            = 3306
SCHEMA          = dbname

要使用模型,只需在 database/models 文件夹中创建一个类,并赋予它以下结构。

namespace App\database\models;

use Odin\database\orm\ORMMapper;

class SimpleModel extends ORMMapper
{
    private $tableName = "teste";
    
    public function __construct()
    {
	parent::__construct();
	parent::setTableName($this->tableName);
    }
}

Select

检索表中所有现有记录

use App\database\models\SimpleModel;

$model = new SimpleModel();

var_dump($model->findAll());

通过 id 检索记录

$model->findById(10);

通过 WHERE 子句检索记录

$model->where(["id" => 10], ">")->get(); // id > 10

$model->where(["id" => [10, 20]], "BETWEEN")->get(); // id BETWEEN 10 AND 20
$model->where(["id" => [10, 20]], "NOT BETWEEN")->get(); // id NOT BETWEEN 10 AND 20 

$model->where(["name" => "User"], "LIKE")->get(); // name LIKE '%User%'
$model->where(["name" => "user"], "LIKE_L")->get(); // name LIKE '%User'
$model->where(["name" => "user"], "LIKE_R")->get(); // name LIKE 'User%'

$model->where(["id" => [1, 2, 3, 4, 5]], "IN")->get(); // id IN (1,2,3,4,5)
$model->where(["id" => [1, 2, 3, 4, 5]], "NOT IN")->get(); // id NOT IN (1,2,3,4,5)

$model->where(["id" => "NULL"], "IS")->get(); // id IS NULL

使用 get() 检索特定字段

$model->where(["id" => 10], "<=")->get("id, name, age");

使用 ORDER BY 排序记录

$model->where(...)->orderBy("id", "ASC")->get();

分组记录

$model->where(...)->groupBy("id")->get();

$model->where(...)->groupBy("id")->having(...)->get();

Insert

要插入记录,只需向模型对象添加新属性并使用 save() 方法。

$model->name = "New User";
$model->email = "user@email.com";
$model->age = 20;

$model->save();

如果成功插入新记录,save 方法必须返回一个 PDOStatement,如果插入失败,则返回 false

Update

要执行更新,您将使用与插入相同的结构,但必须提供 id。它是区分 INSERT 命令和 UPDATE 命令的关键。

$model->id = 25;
$model->name = "Name Updated";

$model->save();

Delete

要执行 DELETE,您必须首先检索要删除的记录,并使用 remove() 方法。如下例所示。

$model->findById(10)->remove();

Joins

您可以为表添加别名以简化其使用,如下例所示。

class SimpleModel extends ORMMapper
{
   private $tableName = "teste";
   private $tableAlias = "t";
	    
   public function __construct()
   {
	parent::__construct();
	parent::setTableName($this->tableName, $this->tableAlias);
   }
}

并按如下方式使用它

$model->innerJoin("another a")->on("a.id = t.fk")->get();
$model->leftJoin("another a")->on("a.id = t.fk")->get();
$model->rightJoin("another a")->on("a.id = t.fk")->get();

$model->innerJoin("another a")
	->on("a.id = t.fk")
	->where(["id" => 10], ">=")
	->get();

实用工具

Flash 消息

Odin\utils\FlashMessages 允许您定义在显示后自动销毁的消息,此功能可用于实现应用程序中的实时通知,请看以下示例。

public function simpleMethod()
{
    //Define uma nova mensagem
    FlashMessages::add("Teste", "Testando mensagens flash");
    
    //Recupera o valor da mensagem
    echo FlashMessages::get("Teste");
    
    //Verifica se há uma mensagem na chave informada
    var_dump(FlashMessages::has("Teste"));
}

全局变量

命名空间 Odin\utils\superglobals 包含用于处理 PHP 的原生全局变量($_GET, $_POST, $_SERVER, $_SESSION, $_COOKIE 和 $_FILES)的类。

请看以下示例。

use Odin\utils\superglobals\Post;
use Odin\utils\FlashMessages;

class Teste extends Controller
{
    public function autenticar()
    {
        $usuario = Post::get("usuario");
        $senha = Post::get("senha");

        if($usuario === "user" && $senha === "pass")
        {
            FlashMessages::add("Sucesso", "Autenticação realizada com sucesso!");
            $this->router->redirectTo("home");
        }
        else
        {
            FlashMessages::add("Erro", "Não foi possível realizar a autenticação");
            $this->router->redirectTo("login");
        }
    }
}

数据过滤器

Odin\utils\Filter 提供了一系列方法用于数据过滤,无论是密码、电子邮件还是普通文本。

use Odin\utils\Filter;
...
public function filtrarDados()
{
    $email = Filter::email(Post::get("email"));
    $senha = Filter::clear(Post::get("senha"));

    var_dump(Filter::isValidEmail($email));
}

头部信息

您还可以使用类 Odin\http\server\Header 来管理您的请求头部信息。

use Odin\http\server\Header;
...
public function getDataAsJSON()
{
    Header::contentType("application/json");

    return [
        ["name" => "John", "age" => 20],
        ["name" => "Doe", "age" => 22]
    ];
}

发送电子邮件

您可以通过使用类 Odin\utils\Mail 来简化使用 mail 函数发送电子邮件的方式,如以下示例所示。

public function enviarEmail()
{
    Mail::from("sender@email.com.br");
    Mail::write("to@email.com.br", "Teste de Envio de Email");
    Mail::headers();
    Mail::message("<b>Hey! Testando envio de email pela Ødin!</b>");

    echo (Mail::send() ? "Email enviado." : "Email não enviado.");
}

集合

集合是实现基于强类型语言(如 Java 和 C#)的数据结构的类,它们被实现以简化对象数组的操作,即 Odin\utils\collections 中包含的所有集合都应与对象一起使用。在 Ødin 的这个版本中,实现了 3 个集合,它们是:ArrayListDictionaryQueue

ArrayList

ArrayList 将多个对象组合成一个类似数字键的数组,如下面的示例所示,但它以面向对象的方式、更加组织和易于操作。

$array = [
    0 => new Usuario()
];

使用 ArrayList

use App\classes\Usuario;
use Odin\utils\collections\ArrayList;
use Odin\utils\Functions;

$arraylist = new ArrayList(Usuario::class);

$arraylist->add(new Usuario());

Functions::debug($arraylist->get(0));

Dictionary

Dictionary 是一个键值对结构,也就是说,需要提供一个字符串类型的键和一个对象作为值,这就像是一个具有非数字键的数组,如下面的示例所示

$array = [
    "nome" => "Odin"
    "tipo" => "Framework"
];

使用 Dictionary

use App\classes\Usuario;
use Odin\utils\collections\Dictionary;

$dictionary = new Dictionary("string", Usuario::class);
$dictionary->add("user1", new Usuario());
$dictionary->add("user2", new Usuario());

echo $dictionary->toJson();

Queue

Queue 是一个队列,也就是说,首先插入的元素将是第一个被删除的元素,无法通过索引或键来检索特定元素,始终是按照升序或降序的方式。

use App\classes\Animal;
use Odin\utils\collections\Queue;

$queue = new Queue(Animal::class);

$queue->add(new Animal("Bolt"));
$queue->add(new Animal("Max"));

\Odin\utils\Functions::debug($queue->peek());