{"id":51321871,"url":"https://github.com/gboquizosanchez/hew","last_synced_at":"2026-07-01T14:30:51.725Z","repository":{"id":365959840,"uuid":"1274501057","full_name":"gboquizosanchez/hew","owner":"gboquizosanchez","description":"Schema-driven Laravel migrations. Define your DB once, generate migrations automatically.","archived":false,"fork":false,"pushed_at":"2026-06-19T15:38:09.000Z","size":25,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T17:29:41.621Z","etag":null,"topics":["artisan","database","laravel","laravel-package","migrations","pest","php","schema"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/gboquizosanchez/hew","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/gboquizosanchez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-19T15:21:46.000Z","updated_at":"2026-06-19T15:37:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gboquizosanchez/hew","commit_stats":null,"previous_names":["gboquizosanchez/hew"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/gboquizosanchez/hew","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gboquizosanchez%2Fhew","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gboquizosanchez%2Fhew/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gboquizosanchez%2Fhew/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gboquizosanchez%2Fhew/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gboquizosanchez","download_url":"https://codeload.github.com/gboquizosanchez/hew/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gboquizosanchez%2Fhew/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35011254,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["artisan","database","laravel","laravel-package","migrations","pest","php","schema"],"created_at":"2026-07-01T14:30:47.765Z","updated_at":"2026-07-01T14:30:51.717Z","avatar_url":"https://github.com/gboquizosanchez.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg/2692.svg\" width=\"100\" alt=\"hew\"\u003e\n\n# `gboquizosanchez/hew`\n\n**Schema as source of truth for Laravel migrations**\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/gboquizosanchez/hew.svg)](https://packagist.org/packages/gboquizosanchez/hew)\n[![Total Downloads](https://img.shields.io/packagist/dt/gboquizosanchez/hew.svg)](https://packagist.org/packages/gboquizosanchez/hew)\n[![PHP](https://img.shields.io/badge/PHP-%5E8.1-777BB4?logo=php\u0026logoColor=white)](https://packagist.org/packages/gboquizosanchez/hew)\n[![Laravel](https://img.shields.io/badge/Laravel-10%20%7C%2011%20%7C%2012%20%7C%2013-FF2D20?logo=laravel\u0026logoColor=white)](https://packagist.org/packages/gboquizosanchez/hew)\n[![License: MIT](https://img.shields.io/badge/License-MIT-22C55E.svg)](LICENSE.md)\n[![PHPStan](https://img.shields.io/badge/PHPStan-Level%2010-blue)](https://phpstan.org/)\n[![Tests](https://img.shields.io/badge/Tests-Pest%20v4-9C27B0)](https://pestphp.com/)\n\n---\n\n*Where is the `avatar` column defined? In migration number 47.*\n*hew makes that a one-line answer.*\n\n\u003c/div\u003e\n\n---\n\n## Why hew?\n\nLaravel migrations are append-only by design — great for versioning, painful for understanding the current state.\n\n| Problem                                 | Without hew                      | With hew                                     |\n|-----------------------------------------|----------------------------------|----------------------------------------------|\n| \"Does `users` have an `avatar` column?\" | ❌ Read through N migrations     | ✅ One glance at `schema.php`                |\n| Adding a column to an existing table    | ❌ Write migration by hand       | ✅ Add it to the schema, run `hew:sync`      |\n| Reviewing the full database structure   | ❌ Mental model from git history | ✅ Single file, fluent API                   |\n| CI check for uncommitted schema drift   | ❌ No built-in mechanism         | ✅ `hew:diff` exits `1` when changes pending |\n| Accidental destructive migration        | ❌ Easy to do                    | ✅ Impossible — hew never emits `DROP`       |\n\n---\n\n## 🚀 Features\n\n- **Single source of truth**: `database/schema.php` is the canonical description of your database.\n- **Additive-only**: hew never emits `DROP COLUMN`, `RENAME COLUMN`, or `DROP TABLE`. Removed columns produce warnings, not data loss.\n- **Fluent column API**: Chainable modifiers — `-\u003enullable()`, `-\u003eunique()`, `-\u003edefault()`, `-\u003ereferences()` and more.\n- **Relation metadata**: Declare `hasMany`, `belongsTo`, `belongsToMany` directly on the table definition. `belongsToMany` auto-generates the pivot table.\n- **Import from existing migrations**: `hew:import` generates `schema.php` from an existing migration history — handles `Schema::create`, `Schema::table`, `dropColumn`, `renameColumn`, `-\u003echange()`, and old-style FK declarations.\n- **Import from a SQL dump**: `hew:import-sql` generates `schema.php` directly from a `mysqldump --no-data` file — useful for projects without Laravel migrations.\n- **CI-friendly**: `hew:diff` exits `1` when changes are pending, making it trivially embeddable in pipelines.\n- **Zero runtime footprint**: `--dev` only. No production dependency.\n\n---\n\n## 📦 Installation\n\n```bash\ncomposer require --dev gboquizosanchez/hew\n```\n\nRegister the service provider manually. In **Laravel 11+**, add it to `bootstrap/providers.php`:\n\n```php\nreturn [\n    // ...\n    \\Boquizo\\Hew\\HewServiceProvider::class,\n];\n```\n\nIn **Laravel 10**, add it to the `providers` array in `config/app.php`:\n\n```php\n'providers' =\u003e [\n    // ...\n    \\Boquizo\\Hew\\HewServiceProvider::class,\n],\n```\n\n**New project** — create `database/schema.php` from scratch (see [Define your schema](#define-your-schema) below).\n\n**Existing project with migrations** — generate `schema.php` from your migration history:\n\n```bash\nphp artisan hew:import\n```\n\n**Existing project without migrations** (or as a double-check against the live DB) — generate `schema.php` from a SQL dump:\n\n```bash\nmysqldump --no-data my_database \u003e schema.sql\nphp artisan hew:import-sql schema.sql\n```\n\n---\n\n## 🔧 Usage\n\n### Define your schema\n\n```php\n\u003c?php\n// database/schema.php\n\nuse Boquizo\\Hew\\Schema\\Column;\nuse Boquizo\\Hew\\Schema\\Schema;\nuse Boquizo\\Hew\\Schema\\Table;\n\nreturn Schema::define([\n\n    Table::make('users')\n        -\u003ecolumns([\n            Column::id(),\n            Column::string('name'),\n            Column::string('email')-\u003eunique(),\n            Column::string('password')-\u003ehidden(),\n            Column::timestamps(),\n        ])\n        -\u003ehasMany('posts')\n        -\u003ebelongsToMany('roles'),        // auto-generates role_user pivot\n\n    Table::make('posts')\n        -\u003ecolumns([\n            Column::id(),\n            Column::foreignId('user_id')-\u003ereferences('users'),\n            Column::string('title'),\n            Column::text('body'),\n            Column::boolean('is_published')-\u003edefault(false),\n            Column::decimal('reading_fee', 10, 2)-\u003enullable(),\n            Column::timestamps(),\n        ])\n        -\u003ebelongsTo('users'),\n\n]);\n```\n\n### Check what would change\n\n```bash\nphp artisan hew:diff\n```\n\n```\n  New table: posts\n  + id\n  + user_id, foreignId, -\u003e users\n  + title, string\n  + body, text\n  + is_published, boolean\n  + reading_fee, decimal, 10,2, nullable\n  + created_at, updated_at\n\n  No destructive changes will be applied automatically.\n```\n\n### Generate the migrations\n\n```bash\nphp artisan hew:sync\n```\n\n---\n\n## 📋 Commands\n\n### `hew:diff`\n\nShows pending changes without writing any files. Exit code `0` if clean, `1` if changes are pending — useful in CI to detect uncommitted schema drift.\n\n| Flag     | Description                        |\n|----------|------------------------------------|\n| `--path` | Path to a non-default `schema.php` |\n\n### `hew:sync`\n\nRuns the diff and generates migration files.\n\n| Flag        | Description                                             |\n|-------------|---------------------------------------------------------|\n| `--force`   | Skip the `[Y/n]` confirmation prompt                    |\n| `--dry-run` | Alias for `hew:diff` — show diff only, generate nothing |\n| `--path`    | Path to a non-default `schema.php`                      |\n\n### `hew:import`\n\nReads existing migrations and generates `database/schema.php`. The reverse of `hew:sync` — useful for bootstrapping hew on an existing project.\n\nHandles `Schema::create`, `Schema::table` (add, drop, rename, change columns), old-style `-\u003eforeign()-\u003ereferences()-\u003eon()` FK declarations, and `Schema::drop`/`dropIfExists`. Files using `DB::statement` (raw SQL) are flagged as warnings. Unsupported column types produce `// TODO` comments.\n\n| Flag       | Description                                                       |\n|------------|-------------------------------------------------------------------|\n| `--path`   | Path to migrations directory (default: `database/migrations`)     |\n| `--output` | Output path for `schema.php` (default: `database/schema.php`)     |\n\n### `hew:import-sql`\n\nGenerates `database/schema.php` directly from a `mysqldump --no-data` SQL file. Useful for legacy projects without Laravel migrations, or as a sanity-check against the live database state.\n\n```bash\nmysqldump --no-data my_database \u003e schema.sql\nphp artisan hew:import-sql schema.sql\n```\n\nType mappings follow Laravel conventions: `tinyint(1)` → `boolean`, `varchar(255)` → `string`, `char(n)` → `string` with length, `deleted_at nullable timestamp` → `softDeletes`, `created_at` + `updated_at` → `timestamps()`. Foreign keys from `CONSTRAINT … FOREIGN KEY` blocks are resolved to `-\u003ereferences()`.\n\n| Argument | Description              |\n|----------|--------------------------|\n| `file`   | Path to the SQL dump     |\n\n| Flag       | Description                                                   |\n|------------|---------------------------------------------------------------|\n| `--output` | Output path for `schema.php` (default: `database/schema.php`) |\n\n---\n\n## 🗂️ Column reference\n\n### Types\n\n| Method                              | Blueprint equivalent                                               |\n|-------------------------------------|--------------------------------------------------------------------|\n| `Column::id()`                      | `$table-\u003eid()`                                                     |\n| `Column::string('name')`            | `$table-\u003estring('name')`                                           |\n| `Column::string('name', 100)`       | `$table-\u003estring('name', 100)`                                      |\n| `Column::text('body')`              | `$table-\u003etext('body')`                                             |\n| `Column::integer('count')`          | `$table-\u003einteger('count')`                                         |\n| `Column::bigInteger('amount')`      | `$table-\u003ebigInteger('amount')`                                     |\n| `Column::decimal('total', 10, 2)`   | `$table-\u003edecimal('total', 10, 2)` — precision and scale required   |\n| `Column::float('ratio')`            | `$table-\u003efloat('ratio')`                                           |\n| `Column::boolean('active')`         | `$table-\u003eboolean('active')`                                        |\n| `Column::json('settings')`          | `$table-\u003ejson('settings')`                                         |\n| `Column::timestamp('paid_at')`      | `$table-\u003etimestamp('paid_at')`                                     |\n| `Column::timestamps()`              | `$table-\u003etimestamps()` — shortcut for `created_at` + `updated_at` |\n| `Column::softDeletes()`             | `$table-\u003esoftDeletes()` — shortcut for `deleted_at`               |\n| `Column::date('birth_date')`        | `$table-\u003edate('birth_date')`                                       |\n| `Column::time('starts_at')`         | `$table-\u003etime('starts_at')`                                        |\n| `Column::foreignId('user_id')`      | `$table-\u003eforeignId('user_id')`                                     |\n| `Column::uuid('token')`             | `$table-\u003euuid('token')`                                            |\n| `Column::ulid('id')`                | `$table-\u003eulid('id')`                                               |\n| `Column::morphs('commentable')`     | `$table-\u003emorphs('commentable')`                                    |\n| `Column::rememberToken()`           | `$table-\u003erememberToken()`                                          |\n\n\u003e **Note:** `Column::enum()` is not supported and throws\n\u003e `UnsupportedColumnTypeException`. Use\n\u003e `Column::string()-\u003ecast(YourEnum::class)` instead —\n\u003e hew stores it as `VARCHAR` in the database.\n\n### Modifiers\n\n| Modifier                | Blueprint effect            | Notes                                                     |\n|-------------------------|-----------------------------|-----------------------------------------------------------|\n| `-\u003enullable()`          | `-\u003enullable()`              |                                                           |\n| `-\u003edefault($value)`     | `-\u003edefault($value)`         |                                                           |\n| `-\u003eprimary()`           | `-\u003eprimary()`               |                                                           |\n| `-\u003eunique()`            | `-\u003eunique()`                |                                                           |\n| `-\u003eindex()`             | `-\u003eindex()`                 |                                                           |\n| `-\u003eunsigned()`          | `-\u003eunsigned()`              | Use on `integer` columns                                  |\n| `-\u003ebig()`               | `bigInteger` / `longText`   | Size modifier — also `-\u003etiny()`, `-\u003esmall()`, `-\u003emedium()`, `-\u003elong()` |\n| `-\u003ereferences('table')` | `-\u003econstrained('table')`    | Use on `foreignId` / `uuid` / `ulid` columns              |\n| `-\u003ereferences()`        | `-\u003econstrained()`           | Auto-infers table name from column name (`user_id` → `users`) |\n| `-\u003eforeign()`           | `foreignId` / `foreignUuid` | Converts `id`→`foreignId`, `uuid`→`foreignUuid`           |\n| `-\u003euseCurrent()`        | `-\u003euseCurrent()`            | Use on `timestamp` columns                                |\n| `-\u003ecascadeOnDelete()`   | `-\u003ecascadeOnDelete()`       | Use after `-\u003ereferences()`                                |\n| `-\u003enullOnDelete()`      | `-\u003enullOnDelete()`          | Use after `-\u003ereferences()`                                |\n| `-\u003erestrictOnDelete()`  | `-\u003erestrictOnDelete()`      | Use after `-\u003ereferences()`                                |\n| `-\u003ehidden()`            | *(model only)*              | Marks column as `$hidden` in the generated Eloquent model |\n| `-\u003ecast(MyEnum::class)` | *(model only)*              | Adds an Eloquent `$cast` entry                            |\n| `-\u003emorphs()`            | `uuidMorphs` / `morphs`     | Converts `uuid` column to `uuidMorphs`                    |\n| `-\u003euuid()`              | `nullableUuidMorphs`        | Converts `morphs` to `uuidMorphs`                         |\n\n---\n\n## 🔗 Table reference\n\n```php\nTable::make('users')\n    -\u003ecolumns([...])               // required — array of Column instances\n    -\u003ehasMany('posts')             // metadata only\n    -\u003ehasOne('profile')            // metadata only\n    -\u003ebelongsTo('companies')       // warns if 'company_id' foreignId column is missing\n    -\u003ebelongsToMany('roles')       // auto-generates pivot table role_user if absent\n```\n\nAll relation methods are **metadata only** — they don't generate columns or migrations by themselves.\n\n`belongsToMany('roles')` on `users` automatically generates a `role_user` pivot table (alphabetical) with the two `foreignId` columns when none exists.\n\n---\n\n## 🔒 Safety guarantees\n\nhew never emits `DROP COLUMN`, `RENAME COLUMN`, or `DROP TABLE`.\n\n| Situation                           | What hew does                      |\n|-------------------------------------|------------------------------------|\n| Column in schema, not in migrations | Generates migration to add it      |\n| Column in migrations, not in schema | **Warning only** — no migration    |\n| Column type changed                 | **Warning only** — no migration    |\n| Table in schema, not in migrations  | Generates `create_table` migration |\n\nWhen you see a warning, write that migration by hand. The cost is one extra file. The benefit is `hew:sync` can never destroy your data.\n\n---\n\n## 🧪 Testing\n\n```bash\ncomposer test\n```\n\nThis package uses [Pest v4](https://pestphp.com/) with the Laravel plugin. Static analysis runs via [PHPStan](https://phpstan.org/) at level 10.\n\n### Troubleshooting\n\nIf you encounter issues:\n\n1. **Check the logs** — Laravel logs may contain helpful error messages.\n2. **Verify requirements** — Ensure PHP ^8.1 and Laravel 10–13 are met.\n3. **Clear cache** — Run `php artisan config:clear` and `php artisan cache:clear`.\n4. **`// TODO` lines in generated schema** — The column type is not natively supported by hew (e.g. `geometry`). Fill it in manually or open an issue.\n5. **Open an issue** — [Report bugs or request features](https://github.com/gboquizosanchez/hew/issues/new).\n\n---\n\n## Contributing\n\nContributions are welcome! Please feel free to:\n\n- 🐛 **Report bugs** via [GitHub Issues](https://github.com/gboquizosanchez/hew/issues/new)\n- 💡 **Suggest features** or improvements\n- 🔧 **Submit pull requests** with bug fixes or enhancements\n- 📖 **Improve documentation** or examples\n\nPlease make sure all tests pass and the code follows PSR-12 before submitting a PR.\n\n---\n\n## Credits\n\n- **Author**: [Germán Boquizo Sánchez](mailto:gboquizo@gestazion.com)\n- **Contributors**: [View all contributors](../../contributors)\n\n---\n\n## 📄 License\n\nThis package is open-source software licensed under the [MIT License](LICENSE.md).\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\nMade with ❤️ for the PHP · Laravel community\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgboquizosanchez%2Fhew","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgboquizosanchez%2Fhew","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgboquizosanchez%2Fhew/lists"}