autoworkflow 3.1.5 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.claude/commands/analyze.md +19 -0
  2. package/.claude/commands/audit.md +26 -0
  3. package/.claude/commands/build.md +39 -0
  4. package/.claude/commands/commit.md +25 -0
  5. package/.claude/commands/fix.md +23 -0
  6. package/.claude/commands/plan.md +18 -0
  7. package/.claude/commands/suggest.md +23 -0
  8. package/.claude/commands/verify.md +18 -0
  9. package/.claude/hooks/post-bash-router.sh +20 -0
  10. package/.claude/hooks/post-commit.sh +140 -0
  11. package/.claude/hooks/pre-edit.sh +129 -0
  12. package/.claude/hooks/session-check.sh +79 -0
  13. package/.claude/settings.json +40 -6
  14. package/.claude/settings.local.json +3 -1
  15. package/.claude/skills/actix.md +337 -0
  16. package/.claude/skills/alembic.md +504 -0
  17. package/.claude/skills/angular.md +237 -0
  18. package/.claude/skills/api-design.md +187 -0
  19. package/.claude/skills/aspnet-core.md +377 -0
  20. package/.claude/skills/astro.md +245 -0
  21. package/.claude/skills/auth-clerk.md +327 -0
  22. package/.claude/skills/auth-firebase.md +367 -0
  23. package/.claude/skills/auth-nextauth.md +359 -0
  24. package/.claude/skills/auth-supabase.md +368 -0
  25. package/.claude/skills/axum.md +386 -0
  26. package/.claude/skills/blazor.md +456 -0
  27. package/.claude/skills/chi.md +348 -0
  28. package/.claude/skills/code-review.md +133 -0
  29. package/.claude/skills/csharp.md +296 -0
  30. package/.claude/skills/css-modules.md +325 -0
  31. package/.claude/skills/cypress.md +343 -0
  32. package/.claude/skills/debugging.md +133 -0
  33. package/.claude/skills/diesel.md +392 -0
  34. package/.claude/skills/django.md +301 -0
  35. package/.claude/skills/docker.md +319 -0
  36. package/.claude/skills/doctrine.md +473 -0
  37. package/.claude/skills/documentation.md +182 -0
  38. package/.claude/skills/dotnet.md +409 -0
  39. package/.claude/skills/drizzle.md +293 -0
  40. package/.claude/skills/echo.md +321 -0
  41. package/.claude/skills/eloquent.md +256 -0
  42. package/.claude/skills/emotion.md +426 -0
  43. package/.claude/skills/entity-framework.md +370 -0
  44. package/.claude/skills/express.md +316 -0
  45. package/.claude/skills/fastapi.md +329 -0
  46. package/.claude/skills/fastify.md +299 -0
  47. package/.claude/skills/fiber.md +315 -0
  48. package/.claude/skills/flask.md +322 -0
  49. package/.claude/skills/gin.md +342 -0
  50. package/.claude/skills/git.md +116 -0
  51. package/.claude/skills/github-actions.md +353 -0
  52. package/.claude/skills/go.md +377 -0
  53. package/.claude/skills/gorm.md +409 -0
  54. package/.claude/skills/graphql.md +478 -0
  55. package/.claude/skills/hibernate.md +379 -0
  56. package/.claude/skills/hono.md +306 -0
  57. package/.claude/skills/java.md +400 -0
  58. package/.claude/skills/jest.md +313 -0
  59. package/.claude/skills/jpa.md +282 -0
  60. package/.claude/skills/kotlin.md +347 -0
  61. package/.claude/skills/kubernetes.md +363 -0
  62. package/.claude/skills/laravel.md +414 -0
  63. package/.claude/skills/mcp-browser.md +320 -0
  64. package/.claude/skills/mcp-database.md +219 -0
  65. package/.claude/skills/mcp-fetch.md +241 -0
  66. package/.claude/skills/mcp-filesystem.md +204 -0
  67. package/.claude/skills/mcp-github.md +217 -0
  68. package/.claude/skills/mcp-memory.md +240 -0
  69. package/.claude/skills/mcp-search.md +218 -0
  70. package/.claude/skills/mcp-slack.md +262 -0
  71. package/.claude/skills/micronaut.md +388 -0
  72. package/.claude/skills/mongodb.md +319 -0
  73. package/.claude/skills/mongoose.md +355 -0
  74. package/.claude/skills/mysql.md +281 -0
  75. package/.claude/skills/nestjs.md +335 -0
  76. package/.claude/skills/nextjs-app-router.md +260 -0
  77. package/.claude/skills/nextjs-pages.md +172 -0
  78. package/.claude/skills/nuxt.md +202 -0
  79. package/.claude/skills/openapi.md +489 -0
  80. package/.claude/skills/performance.md +199 -0
  81. package/.claude/skills/php.md +398 -0
  82. package/.claude/skills/playwright.md +371 -0
  83. package/.claude/skills/postgresql.md +257 -0
  84. package/.claude/skills/prisma.md +293 -0
  85. package/.claude/skills/pydantic.md +304 -0
  86. package/.claude/skills/pytest.md +313 -0
  87. package/.claude/skills/python.md +272 -0
  88. package/.claude/skills/quarkus.md +377 -0
  89. package/.claude/skills/react.md +230 -0
  90. package/.claude/skills/redis.md +391 -0
  91. package/.claude/skills/refactoring.md +143 -0
  92. package/.claude/skills/remix.md +246 -0
  93. package/.claude/skills/rest-api.md +490 -0
  94. package/.claude/skills/rocket.md +366 -0
  95. package/.claude/skills/rust.md +341 -0
  96. package/.claude/skills/sass.md +380 -0
  97. package/.claude/skills/sea-orm.md +382 -0
  98. package/.claude/skills/security.md +167 -0
  99. package/.claude/skills/sequelize.md +395 -0
  100. package/.claude/skills/spring-boot.md +416 -0
  101. package/.claude/skills/sqlalchemy.md +269 -0
  102. package/.claude/skills/sqlx-rust.md +408 -0
  103. package/.claude/skills/state-jotai.md +346 -0
  104. package/.claude/skills/state-mobx.md +353 -0
  105. package/.claude/skills/state-pinia.md +431 -0
  106. package/.claude/skills/state-redux.md +337 -0
  107. package/.claude/skills/state-tanstack-query.md +434 -0
  108. package/.claude/skills/state-zustand.md +340 -0
  109. package/.claude/skills/styled-components.md +403 -0
  110. package/.claude/skills/svelte.md +238 -0
  111. package/.claude/skills/sveltekit.md +207 -0
  112. package/.claude/skills/symfony.md +437 -0
  113. package/.claude/skills/tailwind.md +279 -0
  114. package/.claude/skills/terraform.md +394 -0
  115. package/.claude/skills/testing-library.md +371 -0
  116. package/.claude/skills/trpc.md +426 -0
  117. package/.claude/skills/typeorm.md +368 -0
  118. package/.claude/skills/vitest.md +330 -0
  119. package/.claude/skills/vue.md +202 -0
  120. package/.claude/skills/warp.md +365 -0
  121. package/README.md +135 -52
  122. package/package.json +1 -1
  123. package/system/triggers.md +152 -11
@@ -0,0 +1,321 @@
1
+ # Echo Framework Skill
2
+
3
+ ## Application Setup
4
+ \`\`\`go
5
+ package main
6
+
7
+ import (
8
+ "net/http"
9
+ "os"
10
+
11
+ "github.com/labstack/echo/v4"
12
+ "github.com/labstack/echo/v4/middleware"
13
+ )
14
+
15
+ func main() {
16
+ e := echo.New()
17
+
18
+ // Hide banner in production
19
+ e.HideBanner = os.Getenv("ENV") == "production"
20
+
21
+ // Global middleware
22
+ e.Use(middleware.Logger())
23
+ e.Use(middleware.Recover())
24
+ e.Use(middleware.RequestID())
25
+ e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
26
+ AllowOrigins: []string{"*"},
27
+ AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
28
+ AllowHeaders: []string{echo.HeaderAuthorization, echo.HeaderContentType},
29
+ }))
30
+
31
+ // Custom error handler
32
+ e.HTTPErrorHandler = customErrorHandler
33
+
34
+ // Health check
35
+ e.GET("/health", func(c echo.Context) error {
36
+ return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
37
+ })
38
+
39
+ setupRoutes(e)
40
+
41
+ e.Logger.Fatal(e.Start(":8080"))
42
+ }
43
+ \`\`\`
44
+
45
+ ## Route Groups
46
+ \`\`\`go
47
+ func setupRoutes(e *echo.Echo) {
48
+ api := e.Group("/api/v1")
49
+
50
+ // Public routes
51
+ auth := api.Group("/auth")
52
+ auth.POST("/login", loginHandler)
53
+ auth.POST("/register", registerHandler)
54
+
55
+ // Protected routes
56
+ protected := api.Group("")
57
+ protected.Use(AuthMiddleware)
58
+
59
+ // Users
60
+ users := protected.Group("/users")
61
+ users.GET("", listUsersHandler)
62
+ users.GET("/:id", getUserHandler)
63
+ users.PUT("/:id", updateUserHandler)
64
+ users.DELETE("/:id", deleteUserHandler)
65
+
66
+ // Posts with rate limiting
67
+ posts := protected.Group("/posts")
68
+ posts.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(10)))
69
+ posts.GET("", listPostsHandler)
70
+ posts.POST("", createPostHandler)
71
+ }
72
+ \`\`\`
73
+
74
+ ## Request Binding and Validation
75
+ \`\`\`go
76
+ import "github.com/go-playground/validator/v10"
77
+
78
+ // Custom validator
79
+ type CustomValidator struct {
80
+ validator *validator.Validate
81
+ }
82
+
83
+ func (cv *CustomValidator) Validate(i interface{}) error {
84
+ if err := cv.validator.Struct(i); err != nil {
85
+ return echo.NewHTTPError(http.StatusBadRequest, formatValidationErrors(err))
86
+ }
87
+ return nil
88
+ }
89
+
90
+ // Register validator
91
+ func main() {
92
+ e := echo.New()
93
+ e.Validator = &CustomValidator{validator: validator.New()}
94
+ // ...
95
+ }
96
+
97
+ // Request DTOs
98
+ type CreateUserRequest struct {
99
+ Email string \`json:"email" validate:"required,email"\`
100
+ Name string \`json:"name" validate:"required,min=2,max=100"\`
101
+ Password string \`json:"password" validate:"required,min=8"\`
102
+ }
103
+
104
+ type QueryParams struct {
105
+ Page int \`query:"page" validate:"omitempty,min=1"\`
106
+ PerPage int \`query:"per_page" validate:"omitempty,min=1,max=100"\`
107
+ Sort string \`query:"sort" validate:"omitempty,oneof=asc desc"\`
108
+ }
109
+
110
+ func createUserHandler(c echo.Context) error {
111
+ var req CreateUserRequest
112
+ if err := c.Bind(&req); err != nil {
113
+ return echo.NewHTTPError(http.StatusBadRequest, "invalid request body")
114
+ }
115
+ if err := c.Validate(&req); err != nil {
116
+ return err // Already formatted by custom validator
117
+ }
118
+
119
+ user, err := userService.Create(c.Request().Context(), req)
120
+ if err != nil {
121
+ return err
122
+ }
123
+
124
+ return c.JSON(http.StatusCreated, user)
125
+ }
126
+
127
+ func listUsersHandler(c echo.Context) error {
128
+ var query QueryParams
129
+ if err := c.Bind(&query); err != nil {
130
+ return echo.NewHTTPError(http.StatusBadRequest, "invalid query parameters")
131
+ }
132
+
133
+ // Defaults
134
+ if query.Page == 0 {
135
+ query.Page = 1
136
+ }
137
+ if query.PerPage == 0 {
138
+ query.PerPage = 20
139
+ }
140
+
141
+ users, total, err := userService.List(c.Request().Context(), query)
142
+ if err != nil {
143
+ return err
144
+ }
145
+
146
+ return c.JSON(http.StatusOK, map[string]interface{}{
147
+ "data": users,
148
+ "total": total,
149
+ "page": query.Page,
150
+ "per_page": query.PerPage,
151
+ })
152
+ }
153
+ \`\`\`
154
+
155
+ ## Custom Middleware
156
+ \`\`\`go
157
+ // Auth Middleware
158
+ func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
159
+ return func(c echo.Context) error {
160
+ authHeader := c.Request().Header.Get("Authorization")
161
+ if authHeader == "" {
162
+ return echo.NewHTTPError(http.StatusUnauthorized, "missing authorization header")
163
+ }
164
+
165
+ tokenString := strings.TrimPrefix(authHeader, "Bearer ")
166
+ claims, err := validateToken(tokenString)
167
+ if err != nil {
168
+ return echo.NewHTTPError(http.StatusUnauthorized, "invalid token")
169
+ }
170
+
171
+ // Store in context
172
+ c.Set("userID", claims.UserID)
173
+ c.Set("userRole", claims.Role)
174
+
175
+ return next(c)
176
+ }
177
+ }
178
+
179
+ // Using Echo's JWT middleware
180
+ import "github.com/labstack/echo-jwt/v4"
181
+
182
+ func setupJWTMiddleware() echo.MiddlewareFunc {
183
+ return echojwt.WithConfig(echojwt.Config{
184
+ SigningKey: []byte(os.Getenv("JWT_SECRET")),
185
+ NewClaimsFunc: func(c echo.Context) jwt.Claims {
186
+ return new(CustomClaims)
187
+ },
188
+ SuccessHandler: func(c echo.Context) {
189
+ token := c.Get("user").(*jwt.Token)
190
+ claims := token.Claims.(*CustomClaims)
191
+ c.Set("userID", claims.UserID)
192
+ },
193
+ })
194
+ }
195
+
196
+ // Skipper for selective middleware
197
+ func AuthSkipper(c echo.Context) bool {
198
+ // Skip auth for public routes
199
+ return strings.HasPrefix(c.Path(), "/api/v1/auth")
200
+ }
201
+ \`\`\`
202
+
203
+ ## Error Handling
204
+ \`\`\`go
205
+ func customErrorHandler(err error, c echo.Context) {
206
+ var (
207
+ code = http.StatusInternalServerError
208
+ message = "internal server error"
209
+ )
210
+
211
+ // Check for echo.HTTPError
212
+ var he *echo.HTTPError
213
+ if errors.As(err, &he) {
214
+ code = he.Code
215
+ if m, ok := he.Message.(string); ok {
216
+ message = m
217
+ }
218
+ }
219
+
220
+ // Check for custom errors
221
+ switch {
222
+ case errors.Is(err, ErrNotFound):
223
+ code = http.StatusNotFound
224
+ message = "resource not found"
225
+ case errors.Is(err, ErrUnauthorized):
226
+ code = http.StatusUnauthorized
227
+ message = "unauthorized"
228
+ case errors.Is(err, ErrForbidden):
229
+ code = http.StatusForbidden
230
+ message = "forbidden"
231
+ }
232
+
233
+ // Log internal errors
234
+ if code == http.StatusInternalServerError {
235
+ c.Logger().Error(err)
236
+ }
237
+
238
+ // Send response (check if already committed)
239
+ if !c.Response().Committed {
240
+ c.JSON(code, map[string]string{"error": message})
241
+ }
242
+ }
243
+ \`\`\`
244
+
245
+ ## Testing
246
+ \`\`\`go
247
+ package handler_test
248
+
249
+ import (
250
+ "bytes"
251
+ "encoding/json"
252
+ "net/http"
253
+ "net/http/httptest"
254
+ "testing"
255
+
256
+ "github.com/labstack/echo/v4"
257
+ "github.com/stretchr/testify/assert"
258
+ )
259
+
260
+ func setupTestEcho() *echo.Echo {
261
+ e := echo.New()
262
+ e.Validator = &CustomValidator{validator: validator.New()}
263
+ e.HTTPErrorHandler = customErrorHandler
264
+ setupRoutes(e)
265
+ return e
266
+ }
267
+
268
+ func TestCreateUser(t *testing.T) {
269
+ e := setupTestEcho()
270
+
271
+ tests := []struct {
272
+ name string
273
+ body map[string]interface{}
274
+ wantStatus int
275
+ }{
276
+ {
277
+ name: "valid user",
278
+ body: map[string]interface{}{
279
+ "email": "test@example.com",
280
+ "name": "Test User",
281
+ "password": "password123",
282
+ },
283
+ wantStatus: http.StatusCreated,
284
+ },
285
+ {
286
+ name: "missing email",
287
+ body: map[string]interface{}{
288
+ "name": "Test User",
289
+ "password": "password123",
290
+ },
291
+ wantStatus: http.StatusBadRequest,
292
+ },
293
+ }
294
+
295
+ for _, tt := range tests {
296
+ t.Run(tt.name, func(t *testing.T) {
297
+ body, _ := json.Marshal(tt.body)
298
+ req := httptest.NewRequest(http.MethodPost, "/api/v1/users", bytes.NewBuffer(body))
299
+ req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
300
+ rec := httptest.NewRecorder()
301
+
302
+ e.ServeHTTP(rec, req)
303
+
304
+ assert.Equal(t, tt.wantStatus, rec.Code)
305
+ })
306
+ }
307
+ }
308
+ \`\`\`
309
+
310
+ ## ✅ DO
311
+ - Register a custom validator with \`e.Validator\`
312
+ - Use \`c.Request().Context()\` for passing context to services
313
+ - Use built-in middleware (Logger, Recover, RequestID, CORS)
314
+ - Implement custom HTTPErrorHandler for consistent error responses
315
+ - Use \`echo.NewHTTPError\` for HTTP errors
316
+
317
+ ## ❌ DON'T
318
+ - Don't forget to register validator before using \`c.Validate()\`
319
+ - Don't write response after calling \`next(c)\` in middleware
320
+ - Don't ignore \`c.Response().Committed\` in error handler
321
+ - Don't use \`c.String()\` for JSON responses - use \`c.JSON()\`
@@ -0,0 +1,256 @@
1
+ # Eloquent ORM Skill
2
+
3
+ ## Model Definition
4
+ \`\`\`php
5
+ <?php
6
+
7
+ namespace App\\Models;
8
+
9
+ use Illuminate\\Database\\Eloquent\\{Model, SoftDeletes, Factories\\HasFactory};
10
+ use Illuminate\\Database\\Eloquent\\Casts\\Attribute;
11
+ use Illuminate\\Database\\Eloquent\\Relations\\{HasMany, BelongsTo, BelongsToMany};
12
+
13
+ class User extends Model
14
+ {
15
+ use HasFactory, SoftDeletes;
16
+
17
+ protected $fillable = ['email', 'name', 'status'];
18
+ protected $hidden = ['password', 'remember_token'];
19
+ protected $casts = [
20
+ 'email_verified_at' => 'datetime',
21
+ 'settings' => 'array',
22
+ 'is_admin' => 'boolean',
23
+ ];
24
+
25
+ // Relationships
26
+ public function posts(): HasMany
27
+ {
28
+ return $this->hasMany(Post::class);
29
+ }
30
+
31
+ public function team(): BelongsTo
32
+ {
33
+ return $this->belongsTo(Team::class);
34
+ }
35
+
36
+ public function roles(): BelongsToMany
37
+ {
38
+ return $this->belongsToMany(Role::class)->withTimestamps();
39
+ }
40
+ }
41
+ \`\`\`
42
+
43
+ ## Accessors & Mutators (Laravel 9+)
44
+ \`\`\`php
45
+ class User extends Model
46
+ {
47
+ // Accessor: $user->full_name
48
+ protected function fullName(): Attribute
49
+ {
50
+ return Attribute::make(
51
+ get: fn () => "{$this->first_name} {$this->last_name}",
52
+ );
53
+ }
54
+
55
+ // Mutator: $user->password = 'secret'
56
+ protected function password(): Attribute
57
+ {
58
+ return Attribute::make(
59
+ set: fn (string $value) => bcrypt($value),
60
+ );
61
+ }
62
+
63
+ // Accessor with caching
64
+ protected function postsCount(): Attribute
65
+ {
66
+ return Attribute::make(
67
+ get: fn () => $this->posts()->count(),
68
+ )->shouldCache();
69
+ }
70
+ }
71
+ \`\`\`
72
+
73
+ ## Query Scopes
74
+ \`\`\`php
75
+ class User extends Model
76
+ {
77
+ // Local scope: User::active()->get()
78
+ public function scopeActive(Builder $query): Builder
79
+ {
80
+ return $query->where('status', 'active');
81
+ }
82
+
83
+ // Dynamic scope: User::ofType('admin')->get()
84
+ public function scopeOfType(Builder $query, string $type): Builder
85
+ {
86
+ return $query->where('type', $type);
87
+ }
88
+
89
+ // Chainable: User::active()->ofType('admin')->recent()->get()
90
+ public function scopeRecent(Builder $query): Builder
91
+ {
92
+ return $query->orderBy('created_at', 'desc');
93
+ }
94
+ }
95
+
96
+ // Global Scope (always applied)
97
+ class ActiveScope implements Scope
98
+ {
99
+ public function apply(Builder $builder, Model $model): void
100
+ {
101
+ $builder->where('deleted_at', null);
102
+ }
103
+ }
104
+
105
+ // In model boot method
106
+ protected static function booted(): void
107
+ {
108
+ static::addGlobalScope(new ActiveScope);
109
+
110
+ // Or inline
111
+ static::addGlobalScope('active', fn (Builder $builder) =>
112
+ $builder->where('is_active', true)
113
+ );
114
+ }
115
+ \`\`\`
116
+
117
+ ## Query Patterns
118
+ \`\`\`php
119
+ // Eager loading (prevent N+1)
120
+ $users = User::with(['posts', 'team'])->get();
121
+
122
+ // Nested eager loading
123
+ $users = User::with(['posts.comments', 'posts.tags'])->get();
124
+
125
+ // Conditional eager loading
126
+ $users = User::with(['posts' => fn ($query) =>
127
+ $query->where('published', true)->latest()
128
+ ])->get();
129
+
130
+ // Lazy eager loading
131
+ $users = User::all();
132
+ $users->load('posts'); // Load after initial query
133
+
134
+ // Chunk for large datasets
135
+ User::chunk(100, function ($users) {
136
+ foreach ($users as $user) {
137
+ // Process each user
138
+ }
139
+ });
140
+
141
+ // Cursor for memory efficiency
142
+ foreach (User::cursor() as $user) {
143
+ // One model at a time in memory
144
+ }
145
+
146
+ // Aggregations
147
+ $count = User::where('status', 'active')->count();
148
+ $avg = Order::where('user_id', $id)->avg('total');
149
+ $exists = User::where('email', $email)->exists();
150
+ \`\`\`
151
+
152
+ ## Eloquent Collections
153
+ \`\`\`php
154
+ $users = User::all();
155
+
156
+ // Filter
157
+ $active = $users->where('status', 'active');
158
+ $admins = $users->filter(fn ($user) => $user->isAdmin());
159
+
160
+ // Transform
161
+ $emails = $users->pluck('email');
162
+ $grouped = $users->groupBy('team_id');
163
+ $keyed = $users->keyBy('id');
164
+
165
+ // Aggregate
166
+ $first = $users->first();
167
+ $unique = $users->unique('email');
168
+ $sorted = $users->sortBy('name');
169
+ \`\`\`
170
+
171
+ ## Model Events & Observers
172
+ \`\`\`php
173
+ // Using closures in boot
174
+ protected static function booted(): void
175
+ {
176
+ static::creating(function (User $user) {
177
+ $user->uuid = Str::uuid();
178
+ });
179
+
180
+ static::deleting(function (User $user) {
181
+ $user->posts()->delete();
182
+ });
183
+ }
184
+
185
+ // Observer class (php artisan make:observer UserObserver --model=User)
186
+ class UserObserver
187
+ {
188
+ public function created(User $user): void
189
+ {
190
+ Mail::to($user)->send(new WelcomeEmail($user));
191
+ }
192
+
193
+ public function updating(User $user): void
194
+ {
195
+ if ($user->isDirty('email')) {
196
+ $user->email_verified_at = null;
197
+ }
198
+ }
199
+ }
200
+
201
+ // Register in AppServiceProvider
202
+ User::observe(UserObserver::class);
203
+ \`\`\`
204
+
205
+ ## Factories & Seeders
206
+ \`\`\`php
207
+ // database/factories/UserFactory.php
208
+ class UserFactory extends Factory
209
+ {
210
+ public function definition(): array
211
+ {
212
+ return [
213
+ 'name' => fake()->name(),
214
+ 'email' => fake()->unique()->safeEmail(),
215
+ 'password' => bcrypt('password'),
216
+ ];
217
+ }
218
+
219
+ // States for variations
220
+ public function admin(): static
221
+ {
222
+ return $this->state(fn (array $attributes) => [
223
+ 'is_admin' => true,
224
+ ]);
225
+ }
226
+
227
+ public function withPosts(int $count = 3): static
228
+ {
229
+ return $this->has(Post::factory()->count($count));
230
+ }
231
+ }
232
+
233
+ // Usage
234
+ $user = User::factory()->create();
235
+ $admin = User::factory()->admin()->create();
236
+ $userWithPosts = User::factory()->withPosts(5)->create();
237
+ User::factory()->count(50)->create();
238
+ \`\`\`
239
+
240
+ ## ❌ DON'T
241
+ - Use \`User::all()\` without limits on large tables
242
+ - Forget to eager load relations (N+1 problem)
243
+ - Put business logic in Eloquent models
244
+ - Use \`$model->save()\` without checking return value
245
+ - Store sensitive data without encryption
246
+ - Use raw queries without parameter binding
247
+
248
+ ## ✅ DO
249
+ - Always eager load known relations
250
+ - Use chunks or cursors for large datasets
251
+ - Implement soft deletes for important data
252
+ - Use casts for automatic type conversion
253
+ - Define inverse relationships
254
+ - Use scopes for reusable query logic
255
+ - Use observers for side effects
256
+ - Use factories for testing