{"id":13805854,"url":"https://github.com/zachleigh/yarak","last_synced_at":"2026-01-11T13:41:26.482Z","repository":{"id":57089026,"uuid":"83725289","full_name":"zachleigh/yarak","owner":"zachleigh","description":"Phalcon devtools","archived":false,"fork":false,"pushed_at":"2018-09-22T03:12:34.000Z","size":330,"stargazers_count":28,"open_issues_count":1,"forks_count":11,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-06-23T04:57:52.677Z","etag":null,"topics":["command-line","console","database","database-seeding","devtools","migrations","model-factory","phalcon","seeder"],"latest_commit_sha":null,"homepage":null,"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/zachleigh.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}},"created_at":"2017-03-02T21:12:11.000Z","updated_at":"2024-06-21T22:32:58.000Z","dependencies_parsed_at":"2022-08-20T16:00:32.605Z","dependency_job_id":null,"html_url":"https://github.com/zachleigh/yarak","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachleigh%2Fyarak","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachleigh%2Fyarak/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachleigh%2Fyarak/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachleigh%2Fyarak/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zachleigh","download_url":"https://codeload.github.com/zachleigh/yarak/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":213870459,"owners_count":15650178,"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":["command-line","console","database","database-seeding","devtools","migrations","model-factory","phalcon","seeder"],"created_at":"2024-08-04T01:01:05.629Z","updated_at":"2026-01-11T13:41:26.476Z","avatar_url":"https://github.com/zachleigh.png","language":"PHP","funding_links":[],"categories":["Miscellaneous"],"sub_categories":[],"readme":"# Yarak   \n[![Latest Stable Version](https://poser.pugx.org/zachleigh/yarak/v/stable)](https://packagist.org/packages/zachleigh/yarak)\n[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](//packagist.org/packages/zachleigh/yarak)\n[![Build Status](https://img.shields.io/travis/zachleigh/yarak/master.svg)](https://travis-ci.org/zachleigh/yarak)\n[![Quality Score](https://img.shields.io/scrutinizer/g/zachleigh/yarak.svg)](https://scrutinizer-ci.com/g/zachleigh/yarak/)\n[![StyleCI](https://styleci.io/repos/83725289/shield?style=flat)](https://styleci.io/repos/83725289)     \n\n*yarak - (Falconry) a state of prime fitness in a falcon*    \n  \n#### Laravel inspired Phalcon devtools \n  - Database migrations that rollback step-by-step, reset the database, and refresh the database.\n  - Model factories for easy test data creation.\n  - Database seeders that fill your database with a single command.\n  - Create custom commands in minutes to streamline and personalize your workflow.\n\n## Contents\n  - [Release Notes](#release-notes)\n  - [Install](#install)\n  - [Database](#database)\n    - [Generating Database Directories And Files](#generating-database-directories-and-files)\n    - [Model Factories](#model-factories)\n      - [Defining Factories](#defining-factories)\n      - [Using The Factory Helper](#using-the-factory-helper)\n      - [Making Multiple Model Instances](#making-multiple-model-instances)\n      - [Overriding The Default Attributes](#overriding-the-default-attributes)\n      - [Using Named Factories](#using-named-factories)\n      - [Model Relationships](#model-relationships)\n    - [Database Seeding](#database-seeding)\n      - [Creating Database Seeders](#creating-database-seeders)\n      - [Writing Database Seeders](#writing-database-seeders)\n      - [Using Database Seeders](#using-database-seeders)\n  - [Migrations](#migrations)\n    - [Generating Migrations](#generating-migrations)\n    - [Writing Migrations](#writing-migrations)\n      - [Creating Tables](#creating-tables)\n      - [Updating Tables](#updating-tables)\n      - [The Down Method](#the-down-method)\n    - [Running Migrations](#running-migrations)\n    - [Rolling Back Migrations](#rolling-back-migrations)\n    - [Resetting The Database](#resetting-the-database)\n    - [Refreshing The Database](#refreshing-the-database)\n  - [Custom Commands](#custom-commands)\n    - [Generating Console Directories And Files](#generating-console-directories-and-files)\n    - [Generating Custom Commands](#generating-custom-commands)\n    - [Writing Custom Commands](#writing-custom-commands)\n      - [Command Signature](#command-signature)\n        - [Defining Command Arguments](#defining-command-arguments)\n        - [Defining Command Options](#defining-command-options)\n        - [Accessing Command Arguments And Options](#accessing-command-arguments-and-options)\n      - [Command Output](#command-output)\n    - [Using Custom Commands](#using-custom-commands)\n  - [Calling Yarak In Code](#calling-yarak-in-code)\n  - [Developing](#developing)\n  - [Credits and Contributing](#credits-and-contributing)\n\n## Release Notes\n##### Moving from 1.1.* to 1.2.*\nThe core command wrapper has been extracted to a separate package\n([zachleigh/artisanize](https://github.com/zachleigh/artisanize)). Where possible,\nYarak classes have been maintained for the time being in order to minimize update\nissues. However, some interface type declarations may need to be updated after\nisntalling the new version.\n\n## Install\n### Requirements\nThis package assumes you have the following:\n  - Phalcon \u003e= 3.0\n  - PHP \u003e= 5.6.5\n\n### Install via composer\n```\ncomposer require zachleigh/yarak\n```\n### Register the service\n```php\nuse Yarak\\Kernel;\n\n$di-\u003esetShared('yarak',function () {\n    return new Kernel();\n});\n```\nYarak requires the following config values in this structure:\n```php\n'database' =\u003e [\n    'adapter'     =\u003e '',\n    'host'        =\u003e '',\n    'username'    =\u003e '',\n    'password'    =\u003e '',\n    'dbname'      =\u003e '',\n    'charset'     =\u003e '',\n],\n'application' =\u003e [\n    'appDir'         =\u003e APP_PATH.'/',\n    'commandsDir'    =\u003e APP_PATH.'/console/commands',\n    'consoleDir'     =\u003e APP_PATH.'/console/',\n    'databaseDir'    =\u003e APP_PATH.'/database/',\n    'migrationsDir'  =\u003e APP_PATH.'/database/migrations/',\n    'modelsDir'      =\u003e APP_PATH.'/models/',\n],\n'namespaces' =\u003e [\n    'root' =\u003e '',\n],\n```\nYarak uses your application's config so if your config is already structured this way, simply add the necessary values. If your config strategy differs from this, you have several options. First, you can create a specific Yarak config key and set all values there:\n```php\n'yarak' =\u003e [\n    'database' =\u003e [\n        'adapter'     =\u003e '',\n        'host'        =\u003e '',\n        'username'    =\u003e '',\n        'password'    =\u003e '',\n        'dbname'      =\u003e '',\n        'charset'     =\u003e '',\n    ],\n    'application' =\u003e [\n        'appDir'         =\u003e APP_PATH.'/',\n        'commandsDir'    =\u003e APP_PATH.'/console/commands',\n        'consoleDir'     =\u003e APP_PATH.'/console/',\n        'databaseDir'    =\u003e APP_PATH.'/database/',\n        'migrationsDir'  =\u003e APP_PATH.'/database/migrations/',\n        'modelsDir'      =\u003e APP_PATH.'/models/',\n    ],\n    'namespaces' =\u003e [\n        'root' =\u003e '',\n    ],\n]\n```\nThen, when registering the service pass the config path:\n```php\n$di-\u003esetShared('yarak',function () {\n    return new Kernel('yarak');\n});\n```\nIf the config path is multiple levels deep, use dot notation:\n```php\n'services' =\u003e [\n    'yarak' =\u003e [\n        'database' =\u003e [\n            'adapter'     =\u003e '',\n            'host'        =\u003e '',\n            'username'    =\u003e '',\n            'password'    =\u003e '',\n            'dbname'      =\u003e '',\n            'charset'     =\u003e '',\n        ],\n        'application' =\u003e [\n            'appDir'         =\u003e APP_PATH.'/',\n            'commandsDir'    =\u003e APP_PATH.'/console/commands',\n            'consoleDir'     =\u003e APP_PATH.'/console/',\n            'databaseDir'    =\u003e APP_PATH.'/database/',\n            'migrationsDir'  =\u003e APP_PATH.'/database/migrations/',\n            'modelsDir'      =\u003e APP_PATH.'/models/',\n        ],\n        'namespaces' =\u003e [\n            'root' =\u003e '',\n        ],\n    ]\n]\n```\n```php\n$di-\u003esetShared('yarak',function () {\n    return new Kernel('services.yarak');\n});\n```\nIf you wish to add config values when registering Yarak, pass them as an array and Yarak will merge them into your existing config:\n```php\n$di-\u003esetShared('yarak',function () {\n    return new Kernel([\n        'namespaces' =\u003e [\n            'root' =\u003e '',\n        ],\n    ]);\n});\n```\nLastly, you can pass all config values when registering the service. Pass 'false' as the second parameter to the Kernel constructor to turn off config merging.\n```php\n$di-\u003esetShared('yarak',function () {\n    return new Kernel([\n        'database' =\u003e [\n            'adapter'     =\u003e '',\n            'host'        =\u003e '',\n            'username'    =\u003e '',\n            'password'    =\u003e '',\n            'dbname'      =\u003e '',\n            'charset'     =\u003e '',\n        ],\n        'application' =\u003e [\n            'appDir'         =\u003e APP_PATH.'/',\n            'commandsDir'    =\u003e APP_PATH.'/console/commands',\n            'consoleDir'     =\u003e APP_PATH.'/console/',\n            'databaseDir'    =\u003e APP_PATH.'/database/',\n            'migrationsDir'  =\u003e APP_PATH.'/database/migrations/',\n            'modelsDir'      =\u003e APP_PATH.'/models/',\n        ],\n        'namespaces' =\u003e [\n            'root' =\u003e '',\n        ],\n    ], false);\n});\n```\n\n### Create a yarak file\nIn the project root, create a file called `yarak`. This file needs to do the following:\n  - Autoload all project files and vendor directory files\n  - Load the project services\n  - Resolve the Yarak kernel from the service container and call the `handle` method on it\n\nExample:\n```php\n#!/usr/bin/env php\n\u003c?php\n\nuse Phalcon\\Di\\FactoryDefault;\n\nerror_reporting(E_ALL);\n\ndefine('BASE_PATH', __DIR__);\ndefine('APP_PATH', BASE_PATH . '/app');\n\n/*\n|--------------------------------------------------------------------------\n| Autoload The Application\n|--------------------------------------------------------------------------\n|\n| In order to work properly, Yarak will need both your project files and the\n| vendor folder to be autoloaded.\n|\n*/\ninclude APP_PATH . '/config/loader.php';\n\n/*\n|--------------------------------------------------------------------------\n| Register The App Services\n|--------------------------------------------------------------------------\n|\n| We need to register the app services in order to spin up Yarak. Be sure you\n| have registered Yarak in the services file.\n|\n*/\n$di = new FactoryDefault();\n\ninclude APP_PATH . '/config/services.php';\n\n/*\n|--------------------------------------------------------------------------\n| Handle The Incoming Commands\n|--------------------------------------------------------------------------\n|\n| We'll get the Yarak kernel from the dependency injector and defer to it for \n| command handling.\n|\n*/\n$kernel = $di-\u003egetYarak();\n\n$kernel-\u003ehandle();\n```\nThe above example is included in the project at yarak/src/yarak_example. Copy it into your project with the following command, done from the project root:\n```\ncp vendor/zachleigh/yarak/src/yarak_example yarak\n```\n\nOnce the yarak file exists, make it executable:\n```\nchomd +x yarak\n```\n### Add the database directory to the composer autoloader\nBecause migrations do not follow psr-4 naming conventions, load them with a classmap.\n```\n\"autoload\": {\n    \"classmap\": [\n        \"relative/path/to/database/directory\"\n    ]\n}\n```\nYou may have to dump the composer autoload cache for the change to take affect.\n```\ncomposer dumpautoload\n```\n\nTest to make sure that it is working in the console:\n```\nphp yarak\n```\n\n[Top](#contents)      \n\n## Database\nYarak gives users several helpful database functionalities that make development easier.\n  - [Generating Database Directories And Files](#generating-database-directories-and-files)\n  - [Model Factories](#model-factories)\n    - [Defining Factories](#defining-factories)\n    - [Using The Factory Helper](#using-the-factory-helper)\n    - [Making Multiple Model Instances](#making-multiple-model-instances)\n    - [Overriding The Default Attributes](#overriding-the-default-attributes)\n    - [Using Named Factories](#using-named-factories)\n    - [Model Relationships](#model-relationships)\n  - [Database Seeding](#database-seeding)\n    - [Creating Database Seeders](#creating-database-seeders)\n    - [Writing Database Seeders](#writing-database-seeders)\n    - [Using Database Seeders](#using-database-seeders)\n\n### Generating Database Directories And Files\nAll database and migration functionalites require a standardized file hierarchy. To generate this hirearchy, use the `db:generate` command:\n```\nphp yarak db:generate\n```\nThis will create a database directory at the path set in the Yarak config. The database directory will contain migration, seeder, and factory directories and some file stubs to help you get started. \n\n### Model Factories\nModel factories provide a simple way to create testing data using the [Faker library](https://github.com/fzaninotto/Faker).\n\n#### Defining Factories\nModel factories are located in the `/database/factories` directory. This directory and a stub factory file can be created using the `php yarak db:generate` command.    \n\nTo define a factory, use the `define` method on a variable called `$factory`. The `define` method has the following method signature:\n```php\npublic function define($class, callable $attributes, $name = 'default')\n```\nThe first argument is the full name/namespace of the class. The second argument is a callback that returns an array. This array must contain the data necessary to create the model. The third optional argument is a name for the factory. Setting the name allows you to define multiple factories for a single model.\n\nTo create a simple user model factory:\n```php\nuse App\\Models\\Users;\n\n$factory-\u003edefine(Users::class, function (Faker\\Generator $faker) {\n    return [\n        'username' =\u003e $faker-\u003euserName,\n        'email' =\u003e $faker-\u003eunique()-\u003esafeEmail,\n        'password' =\u003e 'password',\n    ];\n});\n```\n\nTo create a named user model factory:\n```php\nuse App\\Models\\Users;\n\n$factory-\u003edefine(Users::class, function (Faker\\Generator $faker) {\n    return [\n        'username' =\u003e 'myUsername',\n        'email' =\u003e 'myEmail',\n        'password' =\u003e 'myPassword',\n    ];\n}, 'myUser');\n```\n\nThe ModelFactory class responsible for creating model instances extends Phalcon\\Mvc\\User\\Component and has access to the DI and any services registered. To access the ModelFactory class, use the `$factory` variable in the `$attributes` closure.\n```php\nuse App\\Models\\Users;\n\n$factory-\u003edefine(Users::class, function (Faker\\Generator $faker) use ($factory) {\n    return [\n        'username' =\u003e $faker-\u003euserName,\n        'email' =\u003e $faker-\u003eunique()-\u003esafeEmail,\n        'password' =\u003e $factory-\u003esecurity-\u003ehash('password'),\n    ];\n});\n```\n\n#### Using The Factory Helper\nYarak comes with a global `factory` helper function to make creating model instances simple. The factory function returns an instance of ModelFactoryBuilder which can be used to either make or create models. Calling `make` on the returned class simply makes the model class, but does not persist the data in the database. Calling `create` creates the class and persists it in the database.   \n\nMake a user model isntance, but don't persist it:\n```php\nuse App\\Models\\Users;\n\n$user = factory(Users::class)-\u003emake();\n```\n\nCreate a user model and persist it:\n```php\nuse App\\Models\\Users;\n\n$user = factory(Users::class)-\u003ecreate();\n```\n\n#### Making Multiple Model Instances\nIf you require multiple instances of the model class, pass an integer as the second argument to `factory`:\n```php\nuse App\\Models\\Users;\n\n// Make three users\n$users = factory(Users::class, 3)-\u003emake();\n\n// Create three users\n$users = factory(Users::class, 3)-\u003ecreate();\n```\nWhen more than one model is made, an array of models is returned.\n\n#### Overriding The Default Attributes\nTo override the default attributes set in the factory definition, pass an array of overrides to `make` or `create`:\n```php\nuse App\\Models\\Users;\n\n// Make a user with username 'bobsmith' and email 'bobsmith@example.com'\n$user = factory(Users::class)-\u003emake([\n    'username' =\u003e 'bobsmith',\n    'email'    =\u003e 'bobsmith@example.com'\n]);\n\n// Create a user with username 'bobsmith' and email 'bobsmith@example.com'\n$user = factory(Users::class)-\u003ecreate([\n    'username' =\u003e 'bobsmith',\n    'email'    =\u003e 'bobsmith@example.com'\n]);\n```\n\n#### Using Named Factories\nTo use a name factory, pass the name as the second argument to the `factory` function:\n```php\nuse App\\Models\\Users;\n\n// Make a user using the factory named 'myUser'\nfactory(Users::class, 'myUser')-\u003emake()\n\n// Create a user using the factory named 'myUser'\nfactory(Users::class, 'myUser')-\u003ecreate()\n```\n\nTo make multiple instances of a named factory, pass the desired number of instances as the third argument:\n```php\nuse App\\Models\\Users;\n\n// Make three users using the factory named 'myUser'\n$users = factory(Users::class, 'myUser', 3)-\u003emake();\n\n// Create three users using the factory named 'myUser'\n$users = factory(Users::class, 'myUser', 3)-\u003ecreates();\n```\n\n#### Model Relationships\nWhen making model instances that require model relationships to also be built, you have a couple options.   \n\nFirst, you can manually create related models. In this example, we have Posts and Users which have a one-to-many relationship: a post can only belong to one user but a user can have many posts. The posts table contains a `users_id` column that references the `id` column on the users table. Posts table migration:\n```php\n$connection-\u003ecreateTable(\n    'posts',\n    null,\n    [\n        'columns' =\u003e [\n            new Column('id', [\n                'type'          =\u003e Column::TYPE_INTEGER,\n                'size'          =\u003e 10,\n                'unsigned'      =\u003e true,\n                'notNull'       =\u003e true,\n                'autoIncrement' =\u003e true,\n            ]),\n            new Column('title', [\n                'type'    =\u003e Column::TYPE_VARCHAR,\n                'size'    =\u003e 200,\n                'notNull' =\u003e true,\n            ]),\n            new Column('body', [\n                'type'    =\u003e Column::TYPE_TEXT,\n                'notNull' =\u003e true,\n            ]),\n            new Column('users_id', [\n                'type'     =\u003e Column::TYPE_INTEGER,\n                'size'     =\u003e 10,\n                'unsigned' =\u003e true,\n                'notNull'  =\u003e true,\n            ]),\n            new Column('created_at', [\n                'type'    =\u003e Column::TYPE_TIMESTAMP,\n                'notNull' =\u003e true,\n                'default' =\u003e 'CURRENT_TIMESTAMP',\n            ]),\n        ],\n        'indexes' =\u003e [\n            new Index('PRIMARY', ['id'], 'PRIMARY')\n        ],\n        'references' =\u003e [\n            new Reference(\n                'user_idfk',\n                [\n                    'referencedTable'   =\u003e 'users',\n                    'columns'           =\u003e ['users_id'],\n                    'referencedColumns' =\u003e ['id'],\n                ]\n            ),\n        ],\n    ]\n);\n```\nFirst, we need to create factories for both users and posts:\n```php\nuse App\\Models\\Posts;\nuse App\\Models\\Users;\n\n$factory-\u003edefine(Users::class, function (Faker\\Generator $faker) use ($factory) {\n    return [\n        'username' =\u003e $faker-\u003euserName,\n        'email'    =\u003e $faker-\u003eunique()-\u003esafeEmail,\n        'password' =\u003e $factory-\u003esecurity-\u003ehash('password'),\n    ];\n});\n\n$factory-\u003edefine(Posts::class, function (Faker\\Generator $faker) {\n    return [\n        'title' =\u003e $faker-\u003eunique()-\u003esentence(4, true),\n        'body'  =\u003e $faker-\u003eparagraph(4, true),\n    ];\n});\n```\n\nTo create three users with one post each, we could simply loop over newly created users and create a post for each, sending the user id as an attribute override:\n```php\nuse App\\Models\\Posts;\nuse App\\Models\\Users;\n\n$users = factory(Users::class, 3)-\u003ecreate();\n\nforeach ($users as $user) {\n    factory(Posts::class)-\u003ecreate([\n        'users_id' =\u003e $user-\u003eid\n    ]);\n}\n```\nFor multiple posts, simply pass the desired number as the second variable to the factory helper:\n```php\nuse App\\Models\\Posts;\nuse App\\Models\\Users;\n\n$users = factory(Users::class, 3)-\u003ecreate();\n\nforeach ($users as $user) {\n    factory(Posts::class, 3)-\u003ecreate([\n        'users_id' =\u003e $user-\u003eid\n    ]);\n}\n```\n\nAnother way to create relationships is by using a closure returning a relationship in a factory definition:\n```php\nuse App\\Models\\Posts;\nuse App\\Models\\Users;\n\n$factory-\u003edefine(Users::class, function (Faker\\Generator $faker) use ($factory) {\n    return [\n        'username' =\u003e $faker-\u003euserName,\n        'email'    =\u003e $faker-\u003eunique()-\u003esafeEmail,\n        'password' =\u003e $factory-\u003esecurity-\u003ehash('password'),\n    ];\n});\n\n$factory-\u003edefine(Posts::class, function (Faker\\Generator $faker) {\n    return [\n        'title'    =\u003e $faker-\u003eunique()-\u003esentence(4, true),\n        'body'     =\u003e $faker-\u003eparagraph(4, true),\n        'users_id' =\u003e function () {\n            return factory(Users::class)-\u003ecreate()-\u003eid;\n        }\n    ];\n}, 'withUser');\n```\nHere we are using a factory within the factory to create a new user for each new post created. We are also naming the factory 'withUser' for convenience. To create 20 posts made by 20 users, we can simply do this:\n```php\nuse App\\Models\\Posts;\n\nfactory(Posts::class, 'withUser', 20)-\u003ecreate();\n```\n\n### Database Seeding\nDatabase seeding gives you the ability to fill your database with testing data in a matter of seconds.\n\n#### Creating Database Seeders\nTo create an empty database seeder file, use the `make:seeder` command:\n```\nphp yarak make:seeder SeederName\n```\nThis will generate an empty seeder file in /database/seeds. It is recommended to create separate seeder files for individual database tables.\n\n#### Writing Database Seeders\nAll database seeders must have a `run` method where the database seeding logic is defined. In the run method, do whatever is necessary to fill the database table. Using [model factories](#model-factories) makes this process simple to acheive. An example seeder for a users tables might look like this:\n```php\nuse App\\Models\\Users;\nuse Yarak\\DB\\Seeders\\Seeder;\n\nclass UsersTableSeeder extends Seeder\n{\n    /**\n     * Run the database seeds.\n     *\n     * @return void\n     */\n    public function run()\n    {\n        factory(Users::class, 5)-\u003ecreate();\n    }\n}\n```\nRunning this seeder will create five users in the database.    \n\nThe parent Seeder class has a `call` method that will call the `run` method on other seeder files. This allows you to create several seeder files and then make a master DatabaseSeeder that will fill the entire database. We already have a UsersTableSeeder above, so let's now make a PostsTableSeeder:\n```php\nuse App\\Models\\Posts;\nuse App\\Models\\Users;\nuse Yarak\\DB\\Seeders\\Seeder;\n\nclass PostsTableSeeder extends Seeder\n{\n    /**\n     * Run the database seeds.\n     *\n     * @return void\n     */\n    public function run()\n    {\n        $allUsers = Users::find();\n\n        foreach ($allUsers as $user) {\n            factory(Posts::class, 5)-\u003ecreate(['users_id' =\u003e $user-\u003egetId()]);\n        }\n    }\n}\n```\nThis will create 5 posts for each of our users. We can then combine our two seeder files in a master DatabaseSeeder file:\n```php\nuse Yarak\\DB\\Seeders\\Seeder;\n\nclass DatabaseSeeder extends Seeder\n{\n    /**\n     * Run the database seeds.\n     *\n     * @return void\n     */\n    public function run()\n    {\n        $this-\u003ecall(UsersTableSeeder::class);\n        $this-\u003ecall(PostsTableSeeder::class);\n    }\n}\n```\nThis will run each seeder file in the order they are listed. First, we will create five users with the UsersTableSeeder, then for each of those users, we will create five posts with the PostsTableSeeder. \n\n#### Using Database Seeders\nTo run database seeder files, use the `db:seed` command:\n```\nphp yarak db:seed SeederName\n```\nThe default seeder name is 'DatabaseSeeder'.\n\nYou may also use the `--seed` flag with the `migrate:refresh` command:\n```\nphp yarak migrate:refresh --seed --class=SeederName\n```\n:exclamation:**Refreshing the database will remove all data from your database.** This command will drop all tables, run all the migrations again, then fill the database using the given seeder class name. The default value for the seeder name is 'DatabaseSeeder'.    \n\n[Top](#contents)     \n\n## Migrations\nYarak migrations provide a simple, clean way to manage your database.\n  - [Generating Migrations](#generating-migrations)\n  - [Writing Migrations](#writing-migrations)\n    - [Creating Tables](#creating-tables)\n    - [Updating Tables](#updating-tables)\n    - [The Down Method](#the-down-method)\n  - [Running Migrations](#running-migrations)\n  - [Rolling Back Migrations](#rolling-back-migrations)\n  - [Resetting The Database](#resetting-the-database)\n  - [Refreshing The Database](#refreshing-the-database)\n\n### Generating Migrations\nAll migrations are stored in databaseDir/migrations. The databaseDir path may be set when [registering the Yarak service](#register-the-service).     \n\nTo generate migrations, use the `make:migration` command:\n```\nphp yarak make:migration migration_name --create=table_name\n```\nThe migration name must be snake_case and will be used to create the migration file name and class name. For example:\n```\nphp yarak make:migration create_users_table\n```\nUsing the name `create_users_table` will generate a migration class called `CreateUsersTable`. Migration file names are generated using a timestamp and the given name. In this example, the generated file name might look something like this: 2017_03_04_055719_create_users_table.php.   \n\nIf you are creating a new table, using the `--create` flag plus the name of the database table will create a migration file with some additional boiler plate to save a little time.\n```\nphp yarak make:migration create_users_table --create=users\n```\n\n### Writing Migrations\nYarak uses Phalcon's [Database Abstraction Layer](https://docs.phalconphp.com/en/3.0.0/reference/db.html) to interact with the database. This guide will only cover the most common operations. For more detailed information about what is possible, please see the [API Documentation](https://docs.phalconphp.com/en/3.0.1/api/Phalcon_Db_Adapter.html). Because the official Phalcon migrations also use the database abstraction layer, the [Phalcon migration documentation](https://docs.phalconphp.com/en/3.0.1/reference/migrations.html#migration-class-anatomy) may also be useful.   \n\n#### Creating Tables\nTo create a table, use the `$connection` variable's `createTable` method.\n```php\npublic createTable (mixed $tableName, mixed $schemaName, array $definition)\n```\n\nTo create a simple users table, your `up` method might look something like this:\n```php\nuse Phalcon\\Db\\Index;\nuse Phalcon\\Db\\Column;\n\n//\n\npublic function up(Pdo $connection)\n{\n    $connection-\u003ecreateTable(\n        'users',\n        null,\n        [\n            'columns' =\u003e [\n                new Column('id', [\n                    'type'          =\u003e Column::TYPE_INTEGER,\n                    'size'          =\u003e 10,\n                    'unsigned'      =\u003e true,\n                    'notNull'       =\u003e true,\n                    'autoIncrement' =\u003e true\n                ]),\n                new Column('username', [\n                    'type'    =\u003e Column::TYPE_VARCHAR,\n                    'size'    =\u003e 32,\n                    'notNull' =\u003e true\n                ]),\n                new Column('password', [\n                    'type'    =\u003e Column::TYPE_CHAR,\n                    'size'    =\u003e 40,\n                    'notNull' =\u003e true\n                ]),\n                new Column('email', [\n                    'type'    =\u003e Column::TYPE_VARCHAR,\n                    'size'    =\u003e 20,\n                    'notNull' =\u003e true\n                ]),\n                new Column('created_at', [\n                    'type'    =\u003e Column::TYPE_TIMESTAMP,\n                    'notNull' =\u003e true,\n                    'default' =\u003e 'CURRENT_TIMESTAMP'\n                ])\n            ],\n            'indexes' =\u003e [\n                new Index('PRIMARY', ['id'], 'PRIMARY'),\n                new Index('users_username_unique', ['username'], 'UNIQUE'),\n                new Index('users_email_unique', ['email'], 'UNIQUE')\n            ]\n        ]\n    );\n}\n```\n\nThe definition array must contain a `columns` array, and can also include `indexes`, `references`, and `options` arrays. To define columns use Phalcon's [DB Column class](https://docs.phalconphp.com/en/3.0.1/api/Phalcon_Db_Column.html) class, for indexes use the [DB Index class](https://docs.phalconphp.com/en/3.0.1/api/Phalcon_Db_Index.html), and for foreign keys use the [DB Reference class](https://docs.phalconphp.com/en/3.0.1/api/Phalcon_Db_Reference.html).  \n\nFor more information, see the [official documentation](https://docs.phalconphp.com/en/3.0.0/reference/db.html#creating-tables).\n\n#### Updating Tables\nTo modify a column, use the `$connection` variable's `modifyColumn` method:\n```php\npublic modifyColumn (mixed $tableName, mixed $schemaName, Phalcon\\Db\\ColumnInterface $column, [Phalcon\\Db\\ColumnInterface $currentColumn])\n```\nContinuing the example above, our email column size is currently set to 20 which is clearly not big enough. To modify this, we can create a new migration:\n```\nphp yarak make:migration increase_user_email_column_size\n```\nIn the created migration's up method, we can write the following:\n```php\npublic function up(Pdo $connection)\n{\n    $connection-\u003emodifyColumn(\n        'users',\n        null,\n        new Column(\n            'email',\n            [\n                'type' =\u003e Column::TYPE_VARCHAR,\n                'size' =\u003e 70,\n            ]\n        )\n    );\n}\n```\nKeep in mind that when using the Column class, `type` is required.    \n\nTo add additional columns to a table, use the `addColumn` method:\n```php\npublic addColumn (mixed $tableName, mixed $schemaName, Phalcon\\Db\\ColumnInterface $column)\n```\nSo if we want to add an `active` column to our users table, we create a new migration:\n```\nphp yarak make:migration add_active_column_to_users_table\n```\nAnd our migration up method could look like this:\n```php\npublic function up(Pdo $connection)\n{\n    $connection-\u003eaddColumn(\n        'users',\n        null,\n        new Column(\n            'active',\n            [\n                'type'    =\u003e Column::TYPE_CHAR,\n                'size'    =\u003e 1,\n                'notNull' =\u003e true,\n            ]\n        )\n    );\n}\n```\nThe [official documentation](https://docs.phalconphp.com/en/3.0.0/reference/db.html#altering-tables) contains some additional examples and information which may be helpful.\n\n#### The Down Method\nIn order for migraion rollbacks to work, migrations must contain a `down` method where the process described in the `up` method is reversed. To continue our above example, when creating the users table, our down method would use the `dropTable` method:\n```php\npublic function down(Pdo $connection)\n{\n    $connection-\u003edropTable('users');\n}\n```\n\nWhen modifying the email column, we could simply modify the column so that it returns to it's previous state:\n```php\npublic function down(Pdo $connection)\n{\n    $connection-\u003emodifyColumn(\n        'users',\n        null,\n        new Column(\n            'email',\n            [\n                'type' =\u003e Column::TYPE_VARCHAR,\n                'size' =\u003e 20,\n            ]\n        )\n    );\n}\n```\n\nWhen adding the `active` column, use the `dropColumn` method:\n```php\npublic function down(Pdo $connection)\n{\n    $connection-\u003edropColumn('users', null, 'active');\n}\n```\n\n### Running Migrations\nTo run all pending migrations, simply use the Yarak `migrate` command:\n```\nphp yarak migrate\n```\n\nThis will run all migrations that have not yet been run. Migrations that are run at the same time will be in the same 'batch' and will be rolled back together.\n\n### Rolling Back Migrations\n:exclamation:**Before rolling back, be aware that all data in the tables you rollback will be lost.**   \n\nTo rollback the last batch of migrations, call `migrate:rollback`:\n```\nphp yarak migrate:rollback\n```\n\nUse `migrate:rollback` with the optional `--steps` flag to rollback more than one batch.\n```\nphp yarak migrate:rollback --steps=2\n\n// with shortcut\nphp yarak migrate:rollback -s=2\n```\nThis will rollback the last two batches of migrations.\n\n### Resetting The Database\nUsing the `migrate:reset` command will rollback all migrations.   \n\n:exclamation:**Resetting the database will remove all data from your database.** Be sure any data you wish to keep is backed up before proceeding.\n```\nphp yarak migrate:reset\n```\n\n### Refreshing The Database\nRefreshing the database will rollback all migrations and then re-run them all in a single batch.   \n\n:exclamation:**Refreshing the database will remove all data from your database.** Be sure any data you wish to keep is backed up before proceeding.\n```\nphp yarak migrate:refresh\n```\n\nWhen using the `migrate:refresh` command, you may also use the `--seed` flag to run all your [database seeders](#database-seeding) after the database has been refreshed. See [Using Database Seeders](#using-database-seeders) for more information.     \n\n[Top](#contents)   \n\n## Custom Commands\nYarak can also be extended and used as a general command line task runner.\n  - [Generating Console Directories And Files](#generating-console-directories-and-files)\n  - [Generating Custom Commands](#generating-custom-commands)\n  - [Writing Custom Commands](#writing-custom-commands)\n    - [Command Signature](#command-signature)\n      - [Defining Command Arguments](#defining-command-arguments)\n      - [Defining Command Options](#defining-command-options)\n      - [Accessing Command Arguments And Options](#accessing-command-arguments-and-options)\n    - [Command Output](#command-output)\n  - [Using Custom Commands](#using-custom-commands)\n\n### Generating Console Directories And Files\nTo generate all the directories and files necessary for the console component to work, use the `console:generate` command:\n```\nphp yarak console:generate\n```\nThis will create a console directory, a commands directory, an example command, and a Kernel.php file where you can register your custom commands. If the config value `namespaces =\u003e root` is set, Yarak will use file path information and the set root namespace to automatically generate namespaces. If you use a non-standard namespace, set `namespaces =\u003e console` as shown below.  \n\n### Generating Custom Commands\nBefore generating a custom command, be sure to include `consoleDir` in your config. You may also register a console namespace if the automatically generated namespaces are incorrect. By default, custom commands with be in the defined console directory in a folder called `commands`. You can override this by registering a `commandsDir`.\n```php\n'application' =\u003e [\n    //\n    'consoleDir' =\u003e APP_PATH.'/console/'\n],\n'namespaces' =\u003e [\n    //\n    'console' =\u003e 'App\\Console'\n],\n});\n```\nDo not forget to register your console namespaces with the Phalcon loader.\n\nOnce `consoleDir` is registered, use the `make:command` command to generate a custom command stub.\n```\nphp yarak make:command CommandName\n```\n\n### Writing Custom Commands\nA command class has three components: a signature, a description, and a handle method.\n```php\nnamespace App\\Console\\Commands;\n\nuse Yarak\\Console\\Command;\n\nclass ExampleCommand extends Command\n{\n    /**\n     * The command signature.\n     *\n     * @var string\n     */\n    protected $signature = 'namespace:name {argument} {--o|option=default}';\n\n    /**\n     * The command description.\n     *\n     * @var string\n     */\n    protected $description = 'Command decription.';\n\n    /**\n     * Handle the command.\n     */\n    protected function handle()\n    {\n        // handle the command\n    }\n}\n```\n`signature` is where you define your command's name, arguments, and options. This is discussed in detail below. `description` is where you can set a description message for your command to be displayed when using the console. The `handle` method will be called when the command is fired and is where you should write the logic for your command. It may be useful to extract the bulk of your logic to a separate service class.\n\n#### Command Signature\nThe command signature is written in the same way that the command will be used in the console and consists of three parts: the command name, arguments, and options. The command name must come first in the signature and can be namespaced by prefixing the command name with a namespace followed by a colon (':'):\n```php\nprotected $signature = 'namespace:name';\n```\nArguments and options are enclosed in curly braces and follow the command name. Options are prefixed by two dashes ('--').\n\n##### Defining Command Arguments\nA standard argument consists of the argument name wrapped in curly braces:\n```php\nprotected $signature = 'namespace:name {arg} {--option}'\n```\nThe argument name, `arg` in the example above, is used to access the argument value via the [`argument` method](#accessing-command-arguments-and-options).   \n\nTo make an argument optional, append a question mark ('?') to the argument name:\n```php\nprotected $signature = 'namespace:name {arg?} {--option}'\n```\n     \nTo give the argument a default value, separate the argument name and the default value with an equals sign ('='):\n```php\nprotected $signature = 'namespace:name {arg=default} {--option}'\n```\nIf no value is provided for the argument, the default value will be used.   \n\nIf the argument is in array form, append an asterisk ('*') to the argument name:\n```php\nprotected $signature = 'namespace:name {arg*} {--option}'\n```\nArguments can then be passed to the command by space separating them:\n```\nphp yarak namespace:name one two three\n```\nThis will set the value of `arg` to `['one', 'two', 'three']`.   \n\nArgument arrays can also be set as optional:\n```php\nprotected $signature = 'namespace:name {arg?*} {--option}'\n```\nWhen accessing optional argument arrays, arguments that have not been passed equal an empty array.\n     \nIt is often helpful to provide a description with an argument. To do this, add a colon (':') after the argument definition and append the description:\n```php\nprotected $signature = 'namespace:name {arg=default : Argument description} {--option}'\n```\n\n##### Defining Command Options\nA standard option consists of the option, prefixed by two dashes ('--'), wrapped in curly braces:\n```php\nprotected $signature = 'namespace:name {argument} {--opt}'\n```\nThe option name, `opt`, is used to access the argument value via the [`option` method](#accessing-command-arguments-and-options). Standard options do not take values and act as true/false flags: the presence of the option when the command is called sets its value to true and if it is not present, the value is false.  \n\nTo define an option with a required value, append an equals sign ('=') to the option name:\n```php\nprotected $signature = 'namespace:name {argument} {--opt=}'\n```\n     \nTo set a default value, place it after the equals sign:\n```php\nprotected $signature = 'namespace:name {argument} {--opt=default}'\n```\n    \nOptions may also have shortcuts to make them easier to remember and use. To set a shortcut, prepend it to the command name and separate the two with a pipe ('|'):\n```php\nprotected $signature = 'namespace:name {argument} {--o|opt}'\n```\nNow, the option may be called inthe standard way:\n```\nphp yarak namespace:name argument --opt\n```\nOr by using the shortcut:\n```\nphp yarak namespace:name argument -o\n```\n\nOptions may also be passed as arrays:\n```php\nprotected $signature = 'namespace:name {argument} {--opt=*}'\n```\nWhen passing options arrays, each value must be prefixed by the option name:\n```\nphp yarak namespace:name argument --opt=one --opt=two --opt=three\n```\nThe value of `opt` will be set to `['one', 'two', 'three']`.   \n\nJust like with arguments, the option description can best by appending a colon (':') and the description to the option name definiton:\n```php\nprotected $signature = 'namespace:name {argument} {--o|opt : option description.}'\n```\n\n#### Accessing Command Arguments And Options\nTo access arguments in the handle method, use the `argument` method:. If an argument name is given, it will return the value of the argument and if nothing is passed, it will return an array of all arguments:\n```php\nprotected function handle()\n{\n    $arg = $this-\u003eargument('arg'); // passed value of arg\n\n    $allArguments = $this-\u003eargument(); // array of all arguments\n}\n```\n    \nThe `option` method works in the exact same way:\n```php\nprotected function handle()\n{\n    $opt = $this-\u003eoption('opt'); // passed value of opt\n\n    $allOptions = $this-\u003eoption(); // array of all options\n}\n```\nThere are also `hasArgument` and `hasOption` methods on the command object:\n```php\nprotected function handle()\n{\n    $argExists = $this-\u003ehasArgument('exists');  // true\n\n    $optExists = $this-\u003ehasOption('doesntExist');  // false\n}\n```\n\n#### Asking for confirmation\nThe `confirm` method can be used to ask the user for a simple confirmation\n```php\nif ($this-\u003econfirm('Do you wish to continue? ')) {\n    //\n}\n```\n\n#### Ask a question\nIn case an open question needs to be prompted the user, the `ask` method can be used.\nThe second argument provides a default fallback\n```php\n$name = $this-\u003eask('What is your name?', 'Nobody');\n```\n\n#### Ask for a password\nThe user answer can be hidden by using the `askPassword` method\n```php\n$password = $this-\u003easkPassword('Please type the password');\n```\n\n#### Choosing from a list\nThe `choose` method only allows an answer from a predefined list of choices\n```php\n$car = $this-\u003echoose('What is your favourite car?', ['Ferrari', 'Lamborghini', 'Maserati'], 1);\n```\n\n#### Auto-completion\nThe `anticipate` method can provide the user some auto-completion help when starting to write.\nThe user can still choose any answer, regardless of the auto-completion hints\n```php\n$food = $this-\u003eanticipate('What is your favourite food?', ['Pizza', 'Pasta', 'Lasagna'], 'Mozzarella');\n```\n\n#### Multi choice\nWhen the user should be allowed to choose more than a single answer, the `choice` method allows to select them from a list\n```php\n$colors = $this-\u003echoice('What are your favourite colors (defaults to blue and red)?', ['Blue', 'Red', 'Green'], '0,1');\n```\n\n#### Command Output\nEvery command has an `output` variable stored on the object that has several methods to help write output to the console.   \n\nThe `write` method outputs plain unformatted text, `writeInfo` outputs green text, `writeError` outputs red text, and `writeComment` outputs yellow text:\n```php\nprotected function handle()\n{\n    $this-\u003eoutput-\u003ewrite('Message'); // plain text\n\n    $this-\u003eoutput-\u003ewriteInfo('Message');  // green text\n\n    $this-\u003eoutput-\u003ewriteError('Message');  // red text\n\n    $this-\u003eoutput-\u003ewriteComment('Message');  // yellow text\n}\n```\n    \nThe output variable is a simple wrapper around Symfony's output class. To access this class, use the `getOutputInterface` method:\n```php\nprotected function handle()\n{\n    $output = $this-\u003egetOutputInterface(); // $output is instance of Symfony\\Component\\Console\\Output\\OutputInterface\n}\n```\n\nKeep in mind that the Yarak command class simply wraps up the Symfony console component. All Symfony command features are available on your custom command object. See the [Symfony console component documentation](http://symfony.com/doc/current/components/console.html) for more details.\n\n### Using Custom Commands\nBefore using your custom command, you must register it in the command Kernel `$commands` array:\n```php\nuse Yarak\\Console\\ConsoleKernel;\nuse App\\Console\\Commands\\ExampleCommand;\nuse App\\Comsone\\Commands\\YourCustomCommand;\n\nclass Kernel extends ConsoleKernel\n{\n    /**\n     * Your custom Yarak commands.\n     *\n     * @var array\n     */\n    protected $commands = [\n        ExampleCommand::class,\n        YourCustomCommand::class\n    ];\n}\n\n```\nOnces registered, the commands may be used like any other Yarak command:\n```\nphp yarak namespace:name arg --opt\n```\n\n[Top](#contents)  \n\n## Calling Yarak In Code\nTo call a Yarak command from your codebase, use the Yarak::call static method.\n```php\npublic static function call($command, array $arguments = [], \\Phalcon\\DiInterface $di = null)\n```\nFor example, to call `migrate:rollback --steps=2`:\n```php\nuse Yarak\\Yarak;\n\nYarak::call('migrate:rollback', [\n    '--steps'    =\u003e 2,\n]);\n```\n\nYarak will use the default DI to get its settings. If the resolved default DI is not working, pass an instance of Phalcon\\Di as the third argument to the call method:\n```php\nuse Yarak\\Yarak;\n\nYarak::call('migrate:rollback', [\n    '--steps'    =\u003e 2,\n], $di);\n```\n\nIf you are running PHP 5.6 or lower, using the static call method may result in the following error message: \n```\nCannot bind an instance to a static closure\n```\nTo avoid this error, pass the $di as the third variable to Yarak::call as shown above.   \n\n[Top](#contents)    \n\n## Developing\nTo set up the project locally for development, clone the repo and run `composer install` from the project root directory. Create the necessary database and enter your database configuration details in both `codeception.yml` and `app/config/config.php`. Run the full test suite with `php vendor/codeception/codeception/codecept run` or use the following composer scripts:\n  - `composer test`: run the full test suite\n  - `composer testf`: run only the functional tests\n  - `composer testu`: run only the unit tests      \n    \n[Top](#contents)\n\n## Credits and Contributing\nThis project is largely inspired by the [Laravel project](https://github.com/laravel). Some portions of code in Yarak were taken directly from the Laravel project. Many thanks to @taylorotwell and the rest of the Laravel contributors.   \n\nContributions are more than welcome. Fork, improve and make a pull request. For bugs, ideas for improvement or other, please create an [issue](https://github.com/zachleigh/yarak/issues).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzachleigh%2Fyarak","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzachleigh%2Fyarak","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzachleigh%2Fyarak/lists"}