attokit/attobox

简单的PHP框架,用于开发。提供HTTP请求和响应处理、路由、资源构建和基本ORM支持。

v1.0.6 2024-07-22 04:43 UTC

This package is auto-updated.

Last update: 2024-09-14 09:22:39 UTC


README

简单的PHP框架,用于开发。提供HTTP请求和响应处理、路由、资源构建和基本ORM支持。

用法

使用composer

$ cd /your/web/root
$ composer require attokit/attobox

这将在您的网站根目录中生成vendor文件夹。

您的网站根目录需要以下文件夹

/your/web/root
    /app        #different actions for your website
    /assets     #all static resources, e.g., images, js
    /library    #db, custom PHP Class files
    /page       #PHP pages need to show directly
    /record     #ORM Record Class files for each table
    /route      #routes file
        /Web.php    #basic route extended from base route

您还需要在网站根目录中以下文件

/your/web/root
    /.htaccess
    /index.php

.htaccess用于Apache服务器,因为所有HTTP请求都将通过index.php,所以...

# ./.htaccess

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /

    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

index.php是网站入口。在此文件中,您需要引入attobox框架的start.php

# ./index.php

require_once(__DIR__."/vendor/attokit/attobox/src/start.php");
Box::start([
    //all init config goes here
    "WEB_PROTOCOL"  => "http",
    "WEB_DOMAIN"    => "localhost",
    "WEB_IP"        => "127.0.0.1",
    "WEB_DOMAIN_AJAXALLOWED" => "localhost",
    //...
]);

/route/Web.php需要引入。在此文件中,您必须将所有自定义控制器定义为\Atto\Box\route\Web类的公共方法。

# ./route/Web.php

namespace Atto\Box\route;

class Web extends Base
{
    /**
     * this will define a controller named index
     * @param Array $args URI array
     * if request url == https://your.domain/index/foo/bar
     * then $args = ["foo", "bar"]
     */
    public function index(...$args)
    {
        $rtn = [
            "hello" => "world"
        ];

        //default response type is html
        return "string";    //echo "string"
        return $rtn;        //echo "{'hello': 'world'}"

        //you can assign response type by using query string

        //...?format=json
        return "string";    //{error:false, errors:[], data:'string'}
        return $rtn;        //{error:false, errors:[], data: {hello: 'world'}}
        //or
        Response::json($rtn);

        //...?format=dump
        return $rtn;        //var_dump($rtn)
        //or
        Response::dump($rtn);

        //...?format=str
        return $rtn;        //echo "{hello:'world'}"
        //or
        Response::str($rtn);

        /**
         * !!! Use return method
         * !!! Response::[method] is NOT Recommand
         */

        //you can response a PHP page like using view
        //page file recommand in /page, but you can put it anywhere
        $page = path_find("page/someView.php");
        Response::page($page, [
            //you can access these params in view page
            "rtn" => $rtn,
            //...
        ]);

        //you can response a code
        Response::code(500);

        //other response usage such as headers, 
        //you can check the Response Class in 
        // vendor/attokit/attobox/src/Response.php
    }
}

现在您可以通过https://your.domain/index请求您的网站。

文件夹

/app

每个/app/[appname]文件夹需要一个PHP类文件Appname.php。必须扩展自\Atto\Box\App。每个/app/[appname]的目录结构如下

/app/appname
    /assets
    /library
    /page
    /record
    /Appname.php

基本上,每个应用程序都可以视为虚拟主机。这些文件夹的用法与网站根目录中的文件夹相同。

/app/appname/Appname.php必须扩展自Atto\Box\App,此类的每个公共方法都可以作为控制器请求,如https://your.domain/appname[/method]

# ./app/appname/Appname.php

namespace Atto\Box\App;

use Atto\Box\App;

class Appname extends App
{
    //default route(controller)
    //https://your.domain/appname
    public function defaultRoute(...$args)
    {
        return "appname/indexController";
    }

    //custom route(controller)
    //https://your.domain/appname/foobar
    public function foobar(...$args)
    {
        return "appname/customController";
    }
}

/assets

所有静态资源都应该存储在这里。例如图像、js、css等。您可以创建任何喜欢的文件夹。

默认资源路由是src,您可以请求存储在资产文件夹中的任何资源。如果您有一个图像文件/assets/image/img01.jpg,则可以像这样请求它:https://your.domain/src/image/img01.jpg。您还可以通过查询字符串稍作调整图像,例如:https://your.domain/src/image/img01.jpg?thumb=128,128

您可以在vendor/attokit/attobox/src/modules/resource中查看attobox支持的Mimes。

/library

您可以在其中创建自己的类。需要命名空间Atto\Box。如果此文件夹在/app/appname中,则命名空间应该是Atto\Box\App\appname

/page

简单的PHP页面可以直接导出。可以像这样请求:https://your.domain/page/pagename,如果在应用程序文件夹中请求,则像:https://your.domain/appname/page/pagename

/record

ORM支持

/route

自定义路由类文件。公共方法可以作为控制器请求。

特殊路由文件Web.phpDbm.phpSrc.phpUac.php,不能使用这些文件名。

ORM支持

Attobox提供简单的ORM支持。仅适用于个人开发,此框架建议使用sqlite3进行数据库操作。

SQLite文件必须存储在/library/db/app/appname/library/db中,对于高级用法,您还需要在[/app/appname]/library/db/config/dbname.json中创建一些配置参数,此配置json文件的示例可以在vendor/attokit/attobox/src/db/config_sample.json中找到。

Record类必须创建在[/app/appname]/record/dbname/Tablename.php中,必须扩展自Atto\Box\Record。RecordSet类必须在与Record类相同的php文件中定义。Record对象基于表行,RecordSet包含Record对象,它可以用作迭代器,每个项目都是一个Record对象,结果将是一个索引数组。

Record类文件如下

# ./record/usr/Usr.php

namespace Atto\Box\record;

use Atto\Box\Record;
use Atto\Box\RecordSet;

use Atto\Box\Counter;

class Usr extends Record
{
    //generator method for auto increment ids
    //this method only triggered before insert
    public function __generateUid()
    {
        //create uid
        //use Counter in vendor/attokit/attobox/src/modules/Counter
        $uidx = Counter::auto("usr_usr_uid");
        $uidx = str_pad($uidx, 4, "0", STR_PAD_LEFT);
        $uid = "U".$uidx;
        return $uid;
    }

    //automatically process before/after insert/update/delete
    protected function beforeInsert() {return $this;}
    protected function afterInsert($result) {return $this;}
    protected function beforeUpdate() {return $this;}
    protected function afterUpdate($result) {return $this;}
    
}

class UsrSet extends RecordSet
{
    //custom methods
    public function disabled()
    {
        //disabled all usrs in usrset
        $this->setField("enable", 0);
        $this->save();
        return $this;
    }
}

CURD

使用catfan/Medoo作为数据库驱动程序。

常见的CURD方法示例

use Atto\Box\route\Base;
use Atto\Box\Db;
use Atto\Box\db\Table;
use Atto\Box\record\Usr;

class SomeRoute extends Base
{
    //method
    public function dbtest()
    {
        $usrdb = Db::load("sqlite:usr");
        $usrtb = $usrdb->table("usr");
        //or
        $usrtb = Table::load("usr/usr");

        //get recordset
        $usrs = $usrtb->whereEnable(1)->whereName("~","jack")->limit(10)->select();
        //or
        $usrs = $usrtb->query->apply([
            "where" => [
                "enable" => 1,
                "name[~]" => "jack",    //LIKE '%jack%'
            ],
            "limit" => 10
        ])->select();

        //get record
        $usr = $usrtb->whereUid("123")->single();

        //read record field value as property
        $uname = $usr->context["name"];
        //or
        $uname = $usr->name;
        $unames = $usrset->name;    //[uname, uname, ...]
        $extra = $usr->extra;       //json to array

        //iterate recordset
        for ($i=0;$i<count($usrs);$i++) {
            //...
        }
        //or
        $rst = [];
        $usrs->each(function($usr) use (&$rst){
            //...
        });

        //export record object to associate array
        $usrinfo = $usr->export(
            "show",     //export type: ctx, db, form, show, table
            true,       //auto calc virtual field, defined in config.json
            true,       //auto query related table record, defined in config.json
        );

        //edit record
        $usr->setField("fieldname", "value");
        $usr->save();
        //edit recordset multiple edit records
        $usrs->setField([
            "field1" => "val1",
            "field2" => "val2",
            "field3" => [
                "jsonkey" => "jsonval"
            ]
        ], true);   //if contains json field, need be true
        $usrs->save();

        //insert new record
        $newusr = $usrtb->new([
            //init record data
        ]);
        $newusr->setField([
            //record data
        ]);
        $newusr->save();

        //delete, will automatically trigger before/afterDelete()
        $newusr->del();
    }
}