{"id":28802571,"url":"https://github.com/connorabbas/basic-framework","last_synced_at":"2025-09-04T17:53:01.995Z","repository":{"id":41992446,"uuid":"438078323","full_name":"connorabbas/basic-framework","owner":"connorabbas","description":"A Basic PHP MVC Framework - Documentation Included","archived":false,"fork":false,"pushed_at":"2024-11-10T20:34:43.000Z","size":1043,"stargazers_count":6,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-17T05:36:12.951Z","etag":null,"topics":["framework","mvc","php","router"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/connorabbas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-14T01:29:53.000Z","updated_at":"2024-11-10T20:31:56.000Z","dependencies_parsed_at":"2024-11-10T21:22:55.811Z","dependency_job_id":"67a6fae2-c251-4ac7-8d0d-e5cfe2cf8556","html_url":"https://github.com/connorabbas/basic-framework","commit_stats":{"total_commits":237,"total_committers":4,"mean_commits":59.25,"dds":0.4092827004219409,"last_synced_commit":"9daf72deb0d39789cf11d04e8a2ed3888a2b77b0"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"purl":"pkg:github/connorabbas/basic-framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connorabbas%2Fbasic-framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connorabbas%2Fbasic-framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connorabbas%2Fbasic-framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connorabbas%2Fbasic-framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/connorabbas","download_url":"https://codeload.github.com/connorabbas/basic-framework/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connorabbas%2Fbasic-framework/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260515071,"owners_count":23020617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","mvc","php","router"],"created_at":"2025-06-18T08:06:44.854Z","updated_at":"2025-09-04T17:53:01.978Z","avatar_url":"https://github.com/connorabbas.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Basic PHP Framework\n## About\nA full-stack PHP framework that gives you the basics for starting a web project.\n\nPHP 8 is required.\n\n## Key Features\n- MVC architecture\n- Simple routing\n- View templates using [Plates](https://platesphp.com/)\n- Class auto loading and namespaces\n- Dependency Injection Container\n- PDO database wrapper class\n\n---\n# Documentation\n- [Installation](#installation)\n- [Routing](#routing)\n- [Controllers](#controllers)\n- [Dependency Injection Container](#dependency-injection-container)\n- [Views](#views)\n- [Models and Database](#models-and-database)\n- [Helper Functions](#helper-functions)\n- [Environmental and Configuration Data](#environmental-and-configuration-data)\n- [CLI Tools](#cli-tools)\n\n## Installation\nDownload using [Composer](https://getcomposer.org/).\n``` bash command-line\ncomposer create-project connora/basic your-project-name\n```\nThe project `.env` file should be created on install when using composer. An `.env.example` file is included as well.\n\n### Serving Your Site Locally\nIf you want to serve your site locally for quick testing or development and you have PHP installed on your machine, use the \"serve\" command while working in the root of your project.\n\n\u003e__Note:__ this will only serve your site with php, not MySQL.\n\n``` bash command-line\nphp basic serve\n```\n\nAlternatively, use Laragon, Docker, or XAMPP with a vhost configuration.\n\n## Routing\n### Registering\nBy default, you will register your routes within `/routes/main.php`.\n\nYou will have access to the `$this-\u003erouter` property which supplies an instance of the `App\\Core\\Router` class.\n\nThe `App\\Core\\Router` class offers the following register methods that align with the common HTTP verbs:\n``` php\n$this-\u003erouter-\u003eget($uri, $callback);\n$this-\u003erouter-\u003epost($uri, $callback);\n$this-\u003erouter-\u003eput($uri, $callback);\n$this-\u003erouter-\u003epatch($uri, $callback);\n$this-\u003erouter-\u003edelete($uri, $callback);\n```\nThese methods will register your routes as valid endpoints within your application. If you try to access a url/route that isn't registered, a `404` page will be displayed.\n\n### Callback Functions\nThe route's callback will either be a closure (an anonymous function) where you can execute your endpoint logic directly, or a reference to a controller method using an array, where the first item is the fully qualified class you want to reference, and the second item is the method name that will be called.\n``` php\n// Basic route using a closure\n$this-\u003erouter-\u003eget('/home', function () {\n    return 'Hello World';\n});\n// Alternatively, use a controller class and a method to store your logic in\n$this-\u003erouter-\u003eget('/home-alt', [HomeController::class, 'index']);\n```\n### Dynamic Parameters\nYou can set dynamic parameters in your routes uri by prefixing the slug with a hashtag `#`. The parameter's value will be available as an argument variable via your callback function.\n\nUsing within a controller:\n```php\n// Ex: yoursite.com/blog/post/123\n// within /routes/main.php\n$this-\u003erouter-\u003eget('/blog/post/#id', [BlogPostController::class, 'show']);\n\n// within /app/controllers/BlogPostController.php\npublic function show($id)\n{\n    // $id = '123'\n    return View::render('pages.blog.post.single', [\n        'blogPostData' =\u003e $this-\u003eblogPostModel-\u003efindById($id)\n    ]);\n}\n```\nOr using a basic closure:\n``` php\n$this-\u003erouter-\u003eget('/example/#id', function ($id) {\n    return $id\n});\n```\n\n### Batch Registering\nYou can register routes in batches that share similar qualities, such as a controller, a uri prefix, or both. The intent here is to reduce boilerplate code, and provide better organization.\n\nWhen batching routes, it's required to chain on the `batch()` method at the end, which accepts a closure argument containing the routes you want the batch properties to apply to.\n\nBatch related methods available to you:\n\n```php\n$this-\u003erouter-\u003econtroller(string $className);\n$this-\u003erouter-\u003eprefixUri(string $uri);\n$this-\u003erouter-\u003ebatch(callable $closure);\n```\nWhen batching routes with the `prefixUri()` method, the routes within the closure will all be prepended by your defined uri prefix.\n\nFor example:\n```php\n$this-\u003erouter\n    -\u003eprefixUri('/users')\n    -\u003ebatch(function () {\n        $this-\u003erouter\n            // /users GET (show all users)\n            -\u003eget('/', [UserController::class, 'index'])\n            // /users/create GET (form to create a user)\n            -\u003eget('/create', [UserController::class, 'create'])\n            // /users POST (endpoint to store a new user)\n            -\u003epost('/', [UserController::class, 'store'])\n            // /users/123 GET (show a single user)\n            -\u003eget('/#id', [UserController::class, 'show'])\n            // /users/123/edit GET (form to edit user properties)\n            -\u003eget('/#id/edit', [UserController::class, 'edit'])\n            // /users/123 PATCH (endpoint to update user properties)\n            -\u003epatch('/#id', [UserController::class, 'update'])\n            // /users/123 DELETE (endpoint to remove a user record)\n            -\u003edelete('/#id', [UserController::class, 'destroy']);\n    });\n```\nWhen batching routes with the `controller()` method, take note that:\n- The register method's second argument inside the closure will be a string referencing the endpoint method, instead of the default array syntax\n- The `$this-\u003erouter-\u003eview()` method is unavailable within the batch closure, since we are required to reference a controller method\n\nFor example:\n```php\n$this-\u003erouter\n    -\u003econtroller(UserController::class)\n    -\u003ebatch(function () {\n        $this-\u003erouter\n            -\u003eget('/users', 'index')\n            -\u003eget('/users/create', 'create')\n            -\u003epost('/users', 'store')\n            -\u003eget('/users/#id', 'show')\n            -\u003eget('/users/#id/edit', 'edit')\n            -\u003epatch('/users/#id', 'update')\n            -\u003edelete('/users/#id', 'destroy');\n    });\n```\nOr use both:\n```php\n$this-\u003erouter\n    -\u003econtroller(UserController::class)\n    -\u003eprefixUri('/users')\n    -\u003ebatch(function () {\n        $this-\u003erouter\n            -\u003eget('/', 'index')\n            -\u003eget('/create', 'create')\n            -\u003epost('/', 'store')\n            -\u003eget('/#id', 'show')\n            -\u003eget('/#id/edit', 'edit')\n            -\u003epatch('/#id', 'update')\n            -\u003edelete('/#id', 'destroy');\n    });\n```\n\u003e__Note:__ you cannot nest a `batch()` method within itself.\n\n### Form Requests\nThe standard html ` \u003cform\u003e ` tag only accepts ` GET ` and ` POST ` as valid request methods. We can overcome this by using the ` method_spoof(string $method) ` helper function. This requires our form to use the `POST` method request and to specify the \"spoofed\" method inside the form using `PUT`, `PATCH`, or `DELETE`. \n\nFor example:\n```php\n$this-\u003erouter-\u003epatch('/update-example', [ExampleClass::class, 'updateMethod']);\n```\n```html\n\u003c!-- Example form request to update data --\u003e\n\u003cform action=\"/update-example\" method=\"POST\"\u003e\n    \u003c?= csrf() ?\u003e\n    \u003c?= method_spoof('PATCH') ?\u003e\n    \u003clabel class=\"form-label\"\u003eField to update\u003c/label\u003e\n    \u003cinput name=\"exampleField\" value=\"\u003c?= $originalValue ?\u003e\" required\u003e\n    \u003cbutton type=\"submit\" name=\"updateSubmit\"\u003eUpdate\u003c/button\u003e\n\u003c/form\u003e\n```\nIt's also recommended to use the included `csrf()` and `csrf_valid()` helper functions to ensure your requests are safe from any potential [Cross Site Request Forgery](https://owasp.org/www-community/attacks/csrf).\n\n### Organization\nAs your application grows, you will probably want to better organize your routes instead of having them all in the `/routes/main.php` file. By default, you have access to the `$this-\u003erouter` property within any PHP file that resides inside of the `/routes` directory. So feel free to organize any file/folder structure you wish!\n\n## Controllers\n### The Basics\nControllers are classes meant to handle the logic for an incoming HTTP request. Make sure to set your controller methods access modifiers to `public` so the router can execute them successfully. \n\nIt is best practice to only handle the user input, and response logic within your controller methods. If you have more complicated business logic involved in your endpoint, it's recommended to abstract that into another class (typically a `Service` class).\n\nControllers should be named and organized based on the subject matter the request is pertaining too. It is also recommended, but not required to include the word \"Controller\" in your class name.\n\nThere is an example controller class provided.\n\n\u003e__Note:__ Controller methods should only accept dynamic route parameter arguments, the included dependency injection container will only resolve classes established in the constructor.\n\n### Requests / User Input\nThe framework provides a default `App\\Core\\Request` class that can be used to interact with user input within your controllers. The class will provide basic sanitation on the incoming request inputs, and methods for interacting with the data, rather than using PHP's super globals directly.\n\nAvailable `App\\Core\\Request` methods:\n```php\n/**\n * Return an array of all sanitized inputs from the request\n */\n$request-\u003eall();\n\n/**\n * Return the sanitized $_GET value by it's key, optional default value\n */\n$request-\u003eget(string $key, string $default = null);\n\n/**\n * Return the sanitized $_POST value by it's key, optional default value\n */\n$request-\u003epost(string $key, string $default = null);\n\n/**\n * Return the sanitized $_REQUEST value by it's key, optional default value\n */\n$request-\u003einput(string $key, string $default = null);\n```\n\nThe `App\\Core\\Request` class can be instantiated within each controller method that needs it, or by using the the included `request()` helper function:\n```php\n\u003c?php\n\nnamespace App\\Controllers;\n\nuse App\\Core\\Request;\n\nclass ExampleController\n{\n    public function update()\n    {\n        // standard instantiation\n        $request = new Request();\n        $data = $request-\u003einput('data');\n\n        // or with the helper\n        $data = request()-\u003einput('data');\n    }\n}\n\n```\n\n## Dependency Injection Container\nBy default, you can type hint any class into a controller's `__construct()` method to have the container build out the class and it's dependencies for you. The container will use reflection and recursion to automatically instantiate and set all the needed dependencies your classes may have.\n\n```php\n\u003c?php\n\nnamespace App\\Controllers;\n\nuse App\\Core\\View;\nuse App\\Models\\UserModel;\n\nclass UserController\n{\n    private $userModel;\n\n    // utilizing the containers automatic resolution\n    // by type hinting the class we want\n    public function __construct(UserModel $userModel)\n    {\n        $this-\u003euserModel = $userModel;\n    }\n\n    public function index()\n    {\n        $users = $this-\u003euserModel-\u003egetAll();\n\n        return View::render('pages.users.list', ['users' =\u003e $users]);\n    }\n}\n```\n`App\\Core\\Container` class methods available to you:\n```php\n$this-\u003econtainer-\u003eget(string $id);\n$this-\u003econtainer-\u003eset(string $id, callable $callback);\n$this-\u003econtainer-\u003esetOnce(string $id, callable $callback);\n```\nBy default the container is used to easily instantiate a class you need, without you having to worry about instantiating it's dependencies. However, In certain situations your classes may not be resolvable by the container (requiring primitive constructor arguments, etc.), or perhaps you need a more custom implementation of the class returned from the container.\n\nTo manually set a class binding into the container, use the `set()` method, passing in a string reference `$id` (usually the fully qualified class name), and a closure as the `$callback` which should return the new class instance. Whenever your set class needs to be resolved by the container, it will use your registered callback to return it's implementation.\n\nIf you want your configured class to only be instantiated once, and used in all the subsequent references in the container, you can use the `setOnce()` method. There are a few core classes that are set once by default for use throughout the framework.\n\nTo return/resolve a class instance from the container, use the `get()` method. This is what the container uses internally when using automatic resolution with your injected classes.\n\nWhen setting up your manual bindings, you can access the container within the closure using the `$container` argument. This allows you to use `$container-\u003eget()` inside the closure to resolve any dependencies for the class you are returning.\n\nIf you need to manually set up a class or interface and it's binding, you may do so in the `App\\Core\\App` class `containerSetup()` method:\n\n```php\n/**\n * Establish any container class bindings for the application\n */\npublic function containerSetup(): self\n{\n    // included by default\n    $this-\u003econtainer-\u003esetOnce(Config::class, function ($container) {\n        return new Config($_ENV);\n    });\n    $this-\u003econtainer-\u003esetOnce(Request::class, function ($container) {\n        return new Request();\n    });\n    $this-\u003econtainer-\u003esetOnce(DB::class, function ($container) {\n        $dbConfig = config('database', 'main');\n        return new DB(\n            $dbConfig['name'],\n            $dbConfig['username'],\n            $dbConfig['password'],\n            $dbConfig['host']\n        );\n    });\n\n    // reference the actual repository whenever the interface is referenced/injected\n    $this-\u003econtainer-\u003eset(UserRepositoryInterface::class, function ($container) {\n        return new UserRepository($container-\u003eget(UserModel::class));\n    });\n\n    return $this;\n}\n```\n\nIf you don't want to resolve certain classes in your controller's constructor, you can use the included `container()` helper function to access the `get()` method.\n\n```php\n\u003c?php\n\nnamespace App\\Controllers;\n\nuse App\\Core\\View;\nuse App\\Models\\UserModel;\n\nclass ExampleController\n{\n    public function index()\n    {\n        // get/resolve the UserModel class from the container without injecting into the constructor\n        // dependencies automatically resolved, pretty neat\n        $userModel = container(UserModel::class);\n        $user = $userModel-\u003egetById(request()-\u003einput('id'));\n\n        return View::render('pages.example', ['user' =\u003e $user]);\n    }\n}\n```\n\u003e__Note:__ You are not required to use the included DI Container. Feel free to manually instantiate your classes and pass them around wherever needed using traditional dependency injection techniques.\n\n## Views\nBy default, the framework uses [Plates](https://platesphp.com/) for it's view template system. The `App\\Core\\View` class is used as a basic wrapper.\n\n### Static Page?\nThe `App\\Core\\Router` class also has a method for calling your view directly, so you don't have to bother with closures or controllers for your more simple pages:\n``` php\n$this-\u003erouter-\u003eview('/', 'pages.welcome');\n```\n### In Your Controller Method\nWhen referencing your view within a controller, use the static ` App\\Core\\View::render() ` method to return the template content. The method accepts the view file reference (using a period `.` as the nesting delimiter, no file extension) and an array of data variables you want accessible in the view.\n``` php\npublic function index()\n{\n    $foo = 'bar';\n\n    return View::render('pages.example', ['foo' =\u003e $foo]);\n}\n```\n\n## Models and Database\nModels are classes that are meant to interact with your database.\n\n### The DB Class\nThe included `App\\Core\\DB` class acts as a wrapper around [PDO](https://www.php.net/manual/en/intro.pdo.php) and is intended to make connecting to a database and executing your queries easier. As mentioned earlier, the `App\\Core\\DB` class is set once into the container by default using the `database.main` configuration settings. You can change the default options, or setup multiple connections using `/config/database.php` and your application's `.env` file, more on that later.\n\n### Establishing a Connection\nThere are multiple approaches to creating and using a database connection within a PHP web application.\n\nAs stated previously, the `App\\Core\\DB` class is what creates our database connection. There is a default container binding added within: `App\\Core\\App::containerSetup()`, it is commented out by default. With this approach, we can ensure that there is only one database class/connection created per request lifecycle, and it can be easily referenced in the application whenever it is needed.\n\nTo make things easier, your model classes should extend the included `App\\Core\\Model` abstract class to include the `$this-\u003edb` property.\n\n``` php\n\u003c?php\n\nnamespace App\\Models;\n\nuse App\\Core\\Model;\n\n/**\n * $this-\u003edb available to use for your queries\n */\nclass ExampleModel extends Model\n{\n    private $table = 'example';\n\n    public function getAll()\n    {\n        $sql = \"SELECT * FROM $this-\u003etable\";\n        return $this-\u003edb-\u003equery($sql);\n    }\n}\n```\n```php\n\u003c?php\n\nnamespace App\\Controllers;\n\nuse App\\Core\\View;\nuse App\\Models\\ExampleModel;\n\nclass ExampleController\n{\n    private $exampleModel;\n\n    public function __construct(ExampleModel $exampleModel)\n    {\n        $this-\u003eexampleModel = $exampleModel;\n    }\n\n    public function index()\n    {\n        return View::render('pages.example', [\n            'data' =\u003e $this-\u003eexampleModel-\u003egetAll();\n        ]);\n    }\n}\n```\n\nHowever, this may not be the approach you want/need in your application, so feel free to remove the binding or use more traditional dependency injection techniques for your database/models:\n```php\n\u003c?php\n\nnamespace App\\Controllers;\n\nuse App\\Core\\DB;\nuse App\\Models\\ExampleModel;\n\nclass ExampleController\n{\n    private $db;\n\n    public function __construct(DB $db)\n    {\n        // Ex.1\n        // Use the main DB connection that is configured in the container\n        // via the type-hinted constructor argument\n        $this-\u003edb = $db;\n\n        // Ex.2\n        // Create an alternative DB class binding in the container\n        // Useful for models that need a different database connection\n        $this-\u003edb = container('db_alt');\n\n        // Ex.3\n        // Create a connection on the fly\n        $dbConfig = config('database', 'alt');\n        $this-\u003edb = new DB(\n            $dbConfig['name'],\n            $dbConfig['username'],\n            $dbConfig['password'],\n            $dbConfig['host']\n        );\n    }\n\n    public function index()\n    {\n        $exampleModel = new ExampleModel($this-\u003edb);\n\n        return View::render('pages.example', [\n            'data' =\u003e $exampleModel-\u003egetAll();\n        ]);\n    }\n}\n```\n\nThe `App\\Core\\DB` class offers the following methods:\n``` php\n/**\n * Get the established PDO connection\n */\n$this-\u003edb-\u003epdo();\n\n/**\n * Prepares the query, binds the params, executes, and runs a fetchAll()\n */\n$this-\u003edb-\u003equery(string $sql, array $params = []);\n\n/**\n * Prepares the query, binds the params, executes, and runs a fetch()\n */\n$this-\u003edb-\u003esingle(string $sql, array $params = []);\n\n/**\n * Prepares the query, binds the params, and executes the query\n */\n$this-\u003edb-\u003eexecute(string $sql, array $params = []);\n```\n\n### Example\n```php\n\u003c?php\n\nnamespace App\\Models;\n\nuse App\\Core\\Model;\n\nclass UserModel extends Model\n{\n    private $table = 'users';\n\n    public function getAll()\n    {\n        $sql = \"SELECT * FROM $this-\u003etable\";\n        return $this-\u003edb-\u003equery($sql);\n    }\n\n    public function getById($id)\n    {\n        $sql = \"SELECT * FROM $this-\u003etable WHERE id = ?\";\n        return $this-\u003edb-\u003esingle($sql, [$id]);\n    }\n\n    public function getByEmail($email)\n    {\n        $sql = \"SELECT * FROM $this-\u003etable WHERE email = ?\";\n        return $this-\u003edb-\u003esingle($sql, [$email]);\n    }\n\n    public function create($name, $email, $username, $password)\n    {\n        $hashedPwd = password_hash($password, PASSWORD_DEFAULT);\n        $sql = \"INSERT INTO $this-\u003etable(name, email, username, password) \n            VALUES(:name, :email, :username, :password)\";\n\n        return $this-\u003edb-\u003eexecute($sql, [\n            'name' =\u003e $name,\n            'email' =\u003e $email,\n            'username' =\u003e $username,\n            'password' =\u003e $hashedPwd,\n        ]);\n    }\n\n    public function update(int $userId, array $properties)\n    {\n        $setString = '';\n        foreach ($properties as $property =\u003e $value) {\n            $setString .= $property . ' = ' . ':' . $property;\n            if ($property != array_key_last($properties)) {\n                $setString .= ', ';\n            } else {\n                $setString .= ' ';\n            }\n        }\n        $properties['id'] = $userId;\n        $sql = \"UPDATE $this-\u003etable\n            SET $setString\n            WHERE id = :id\";\n\n        return $this-\u003edb-\u003eexecute($sql, $properties);\n    }\n\n    public function delete(int $userId)\n    {\n        $sql = \"DELETE FROM $this-\u003etable WHERE id = ?\";\n        return $this-\u003edb-\u003eexecute($sql, [$userId]);\n    }\n}\n```\nTo follow MVC conventions, and for better organization in your application, it is highly recommended to only use the DB class and execute queries within your model classes.\n\n## Helper Functions\nHelper functions are meant to be accessed anywhere within the application. There are few included with the framework, feel free to add our own as well.\n\n` /app/helpers.php `\n\n## Environmental and Configuration Data\n### The `.env` File\nThe project `.env` file should be created on install when using composer. If not, a provided example file is included to create a new one.\n\nThis file is for your settings variables that may differ from each environment your site is being used (local, staging, production), as well as storing private information you don't want committed to your source code repository, such as API keys, database access credentials, etc. It is added to the `.gitignore` by default.\n\n```env\n# Site Environment\nENV=local\n\n# When the data has spaces\nEXAMPLE_DATA=\"Example Data\"\n```\n\n### Configuration Data\nConfiguration data can be thought of as your site \"settings\". This could include database connections, mail server info, site meta data, etc. This data will be stored in multi-dimensional arrays using `.php` files residing in the `/config` directory.\n\nWhen you need to set configuration settings that are related to your private `.env` data, you can use the `$this-\u003eenv` property to access it's values.\n\nThe `config(string $file, string $key)` helper function is used to access the desired data throughout the application. Using the config file reference as the first argument (without the file extension), and the value key location string as the second argument (using a period `.` as the nesting delimiter for accessing the nested values in the configuration array).\n\n```php\n// get the main database connection host name\n$host = config('database', 'main.host');\n```\n\n## CLI Tools\n\nCreate a controller:\n``` bash command-line\nphp basic new:controller YourControllerName\n```\n\nCreate a model:\n``` bash command-line\nphp basic new:model YourModelName\n```\n\nServe your site locally:\n``` bash command-line\nphp basic serve\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconnorabbas%2Fbasic-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconnorabbas%2Fbasic-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconnorabbas%2Fbasic-framework/lists"}