Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[9.x] Artisan model:show command #43156

Merged
merged 19 commits into from
Jul 15, 2022
Merged

Conversation

jessarcher
Copy link
Member

@jessarcher jessarcher commented Jul 13, 2022

This PR introduces a new model:show command that displays information for a model. It combines information from the database alongside information from Eloquent to give you a complete overview of your model.

This command is especially useful as your application grows and you have multiple migrations for a single table, with all sorts of casts, accessors, etc.

image

Not only does it show columns from the database, but it also displays "virtual" attributes - I.e. accessors/attributes that don't have a matching column in the database (See first_name and last_name in the screenshot).

Note that the column type information depends on the database. For example, PostgreSQL doesn't have unsigned information and SQLite doesn't have bigints or column lengths.

It also conveniently displays model relationships, including those defined in traits.

There is a --verbose (or -v) flag that will also show any default values. It first checks for any defaults specified in the $attributes key of your model before falling back to any default specified in the database.

image

There is also a handy --json flag that will return everything as JSON, which I could imagine useful for tools like https://github.com/barryvdh/laravel-ide-helper, https://github.com/nunomaduro/larastan, and https://laravelshift.com.

JSON Example
{
  "class": "App\\Models\\User",
  "database": "mysql",
  "table": "users",
  "attributes": [
    {
      "name": "id",
      "type": "bigint unsigned",
      "increments": true,
      "nullable": false,
      "default": null,
      "unique": true,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "int"
    },
    {
      "name": "name",
      "type": "string(255)",
      "increments": false,
      "nullable": false,
      "default": "Jess",
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": null
    },
    {
      "name": "email",
      "type": "string(255)",
      "increments": false,
      "nullable": false,
      "default": null,
      "unique": true,
      "fillable": true,
      "hidden": false,
      "appended": null,
      "cast": null
    },
    {
      "name": "email_verified_at",
      "type": "datetime",
      "increments": false,
      "nullable": true,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "datetime"
    },
    {
      "name": "password",
      "type": "string(255)",
      "increments": false,
      "nullable": false,
      "default": null,
      "unique": false,
      "fillable": true,
      "hidden": true,
      "appended": null,
      "cast": null
    },
    {
      "name": "remember_token",
      "type": "string(100)",
      "increments": false,
      "nullable": true,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": true,
      "appended": null,
      "cast": null
    },
    {
      "name": "created_at",
      "type": "datetime",
      "increments": false,
      "nullable": true,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "date"
    },
    {
      "name": "updated_at",
      "type": "datetime",
      "increments": false,
      "nullable": true,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "datetime"
    },
    {
      "name": "foo",
      "type": "string(255)",
      "increments": false,
      "nullable": false,
      "default": "a default",
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "App\\Casts\\CustomCast"
    },
    {
      "name": "bar",
      "type": "string(255)",
      "increments": false,
      "nullable": false,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "accessor"
    },
    {
      "name": "baz",
      "type": "string(255)",
      "increments": false,
      "nullable": false,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": "attribute"
    },
    {
      "name": "team_id",
      "type": "bigint unsigned",
      "increments": false,
      "nullable": false,
      "default": null,
      "unique": false,
      "fillable": false,
      "hidden": false,
      "appended": null,
      "cast": null
    },
    {
      "name": "first_name",
      "type": null,
      "increments": false,
      "nullable": null,
      "default": null,
      "unique": null,
      "fillable": false,
      "hidden": false,
      "appended": true,
      "cast": "accessor"
    },
    {
      "name": "last_name",
      "type": null,
      "increments": false,
      "nullable": null,
      "default": null,
      "unique": null,
      "fillable": false,
      "hidden": false,
      "appended": false,
      "cast": "attribute"
    }
  ],
  "relations": [
    {
      "name": "tokens",
      "type": "MorphMany",
      "related": "Laravel\\Sanctum\\PersonalAccessToken"
    },
    {
      "name": "notifications",
      "type": "MorphMany",
      "related": "Illuminate\\Notifications\\DatabaseNotification"
    }
  ]
}

The results do depend on the current state of the database, so any changes made outside of a migration will be reflected here, but any changes from not-run migrations will not.

I'd also like to acknowledge https://github.com/barryvdh/laravel-ide-helper for some of the logic around finding attributes and relationships, and @nunomaduro for his help with the UI 💅

@lucasgiovanny
Copy link

That's awesome to have! I see myself all the time running to the database to check some model information, especially when you jump from project to project. Great job! 😃

@SjorsO
Copy link
Contributor

SjorsO commented Jul 13, 2022

Looks great!

It would be useful if it also shows indexes that each column has (unique, primary, foreign key). I'm working on a big project where some columns are missing important indexes (sometimes even primary keys). This command could make it easier to find those missing indexes.

Maybe the command could even display a yellow warning banner:

Warning
This table has no primary key index

@imanghafoori1
Copy link
Contributor

I suggest php artisan model:list User and php artisan model:list (with no args) will loop through all the psr-4 paths of the composer.json and show a list of all the Model classes with some basic information.

@nunomaduro
Copy link
Member

@jessarcher I've put this pull request on draft, so you can review my suggestion to the layout: #43170.

@nunomaduro nunomaduro marked this pull request as draft July 13, 2022 12:08
@jessarcher jessarcher force-pushed the feat/artisan-model-show-command branch 2 times, most recently from 95192b6 to 187603c Compare July 13, 2022 13:28
@freekmurze
Copy link
Contributor

Very nice work! 👍

I think it could be worthwile to refactor the column/relation finding logic to it’s own class that the command would use. This way, others can also make use of the gathered information.

It could also be cool to somehow let a model append custom information to the output.

Base automatically changed from feat/console-ui-improvements to 9.x July 14, 2022 14:38
@jessarcher jessarcher force-pushed the feat/artisan-model-show-command branch from 68b5e6d to e1a8ce1 Compare July 15, 2022 07:09
@jessarcher jessarcher force-pushed the feat/artisan-model-show-command branch from e1a8ce1 to 262e65c Compare July 15, 2022 10:12
@jessarcher jessarcher force-pushed the feat/artisan-model-show-command branch from 262e65c to 7256908 Compare July 15, 2022 12:28
@jessarcher jessarcher marked this pull request as ready for review July 15, 2022 12:39
@taylorotwell taylorotwell merged commit ae681a4 into 9.x Jul 15, 2022
@taylorotwell taylorotwell deleted the feat/artisan-model-show-command branch July 15, 2022 13:47
@M6268
Copy link

M6268 commented Jul 22, 2022

A suggestion , add this info as doc inside each model (like laravel-ide-helper does).
whenever migration occurs or manually using the command (model:show) update it in the models.
So we can reduce checking each time by manually querying a command.

@omerbaflah
Copy link

Great work! @jessarcher 👏🏻

@spekulatius
Copy link

This looks awesome, thank you @jessarcher!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.