# Admin Settings Module — Technical Reference

Namespace: `App\Http\Controllers\Admin\settings`
Route prefix: `/{locale}/admin/setting/` — named prefix `admin.setting.`
All routes sit inside the `auth:admin` middleware group.

---

## Routes

### AJAX Helpers (outside `setting/` prefix, named under `admin.`)

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| POST | `/{locale}/admin/getCategroy` | `Admin\MainController@getCategroy` | `admin.getCategroy` |
| POST | `/{locale}/admin/getCountry` | `Admin\MainController@getCountry` | `admin.getCountry` |
| POST | `/{locale}/admin/getCity` | `Admin\MainController@getCity` | `admin.getCity` |
| POST | `/{locale}/admin/getFile` | `Admin\MainController@getFile` | `admin.getFile` |
| POST | `/{locale}/admin/getDistricts` | `Admin\MainController@getDistricts` | `admin.getDistricts` |

> Note: `getCategroy`, `getCountry`, `getCity`, and `getFile` are declared in the route file but the handler shown is `App\Http\Controllers\Admin\MainController` (a separate top-level `Admin` controller, not the settings `MainController`). The `getDistricts` below also has a duplicate registration inside the `setting/` prefix pointing to `settings\MainController`.

### Countries

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/Country` | `CountryController@index` | `admin.setting.Country.index` |
| POST | `setting/Country` | `CountryController@store` | `admin.setting.Country.store` |
| GET/HEAD | `setting/Country/create` | `CountryController@create` | `admin.setting.Country.create` |
| GET/HEAD | `setting/Country/{Country}` | `CountryController@show` | `admin.setting.Country.show` |
| PUT/PATCH | `setting/Country/{Country}` | `CountryController@update` | `admin.setting.Country.update` |
| DELETE | `setting/Country/{Country}` | `CountryController@destroy` | `admin.setting.Country.destroy` |
| GET/HEAD | `setting/Country/{Country}/edit` | `CountryController@edit` | `admin.setting.Country.edit` |

### Cities

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/city` | `CityController@index` | `admin.setting.city.index` |
| POST | `setting/city` | `CityController@store` | `admin.setting.city.store` |
| GET/HEAD | `setting/city/create` | `CityController@create` | `admin.setting.city.create` |
| GET/HEAD | `setting/city/{city}` | `CityController@show` | `admin.setting.city.show` |
| PUT/PATCH | `setting/city/{city}` | `CityController@update` | `admin.setting.city.update` |
| DELETE | `setting/city/{city}` | `CityController@destroy` | `admin.setting.city.destroy` |
| GET/HEAD | `setting/city/{city}/edit` | `CityController@edit` | `admin.setting.city.edit` |

### Districts

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/district` | `DistrictController@index` | `admin.setting.district.index` |
| POST | `setting/district` | `DistrictController@store` | `admin.setting.district.store` |
| GET/HEAD | `setting/district/create` | `DistrictController@create` | `admin.setting.district.create` |
| PUT/PATCH | `setting/district/{district}` | `DistrictController@update` | `admin.setting.district.update` |
| DELETE | `setting/district/{district}` | `DistrictController@destroy` | `admin.setting.district.destroy` |
| DELETE\|GET | `setting/district/delete/{id}` | `DistrictController@delete` | `admin.setting.district.delete` |
| POST | `setting/getDistricts` | `settings\MainController@getDistricts` | `admin.setting.getDistricts` |

### Categories

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/Category` | `CategoryController@index` | `admin.setting.Category.index` |
| POST | `setting/Category` | `CategoryController@store` | `admin.setting.Category.store` |
| GET/HEAD | `setting/Category/create` | `CategoryController@create` | `admin.setting.Category.create` |
| GET/HEAD | `setting/Category/{Category}` | `CategoryController@show` | `admin.setting.Category.show` |
| PUT/PATCH | `setting/Category/{Category}` | `CategoryController@update` | `admin.setting.Category.update` |
| DELETE | `setting/Category/{Category}` | `CategoryController@destroy` | `admin.setting.Category.destroy` |
| GET/HEAD | `setting/Category/{Category}/edit` | `CategoryController@edit` | `admin.setting.Category.edit` |

### Files (Document Type Labels)

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/files` | `FilesController@index` | `admin.setting.files.index` |
| POST | `setting/files` | `FilesController@store` | `admin.setting.files.store` |
| GET/HEAD | `setting/files/create` | `FilesController@create` | `admin.setting.files.create` |
| GET/HEAD | `setting/files/{files}` | `FilesController@show` | `admin.setting.files.show` |
| PUT/PATCH | `setting/files/{files}` | `FilesController@update` | `admin.setting.files.update` |
| DELETE | `setting/files/{files}` | `FilesController@destroy` | `admin.setting.files.destroy` |
| GET/HEAD | `setting/files/{files}/edit` | `FilesController@edit` | `admin.setting.files.edit` |

### Nationalities

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/nationality` | `NationalityController@index` | `admin.setting.nationality.index` |
| POST | `setting/nationality` | `NationalityController@store` | `admin.setting.nationality.store` |
| GET/HEAD | `setting/nationality/create` | `NationalityController@create` | `admin.setting.nationality.create` |
| GET/HEAD | `setting/nationality/{nationality}` | `NationalityController@show` | `admin.setting.nationality.show` |
| PUT/PATCH | `setting/nationality/{nationality}` | `NationalityController@update` | `admin.setting.nationality.update` |
| DELETE | `setting/nationality/{nationality}` | `NationalityController@destroy` | `admin.setting.nationality.destroy` |
| GET/HEAD | `setting/nationality/{nationality}/edit` | `NationalityController@edit` | `admin.setting.nationality.edit` |

### Type Settings (Lookup Category Groups)

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/typesetting` | `TypeSettingController@index` | `admin.setting.typesetting.index` |
| POST | `setting/typesetting` | `TypeSettingController@store` | `admin.setting.typesetting.store` |
| GET/HEAD | `setting/typesetting/create` | `TypeSettingController@create` | `admin.setting.typesetting.create` |
| PUT/PATCH | `setting/typesetting/{typesetting}` | `TypeSettingController@update` | `admin.setting.typesetting.update` |
| DELETE | `setting/typesetting/{typesetting}` | `TypeSettingController@destroy` | `admin.setting.typesetting.destroy` |
| DELETE\|GET | `setting/typesetting/delete/{id}` | `TypeSettingController@delete` | `admin.setting.typesetting.delete` |

### Services

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/service` | `ServiceController@index` | `admin.setting.service.index` |
| POST | `setting/service` | `ServiceController@store` | `admin.setting.service.store` |
| GET/HEAD | `setting/service/create` | `ServiceController@create` | `admin.setting.service.create` |
| GET/HEAD | `setting/service/{service}` | `ServiceController@show` | `admin.setting.service.show` |
| PUT/PATCH | `setting/service/{service}` | `ServiceController@update` | `admin.setting.service.update` |
| DELETE | `setting/service/{service}` | `ServiceController@destroy` | `admin.setting.service.destroy` |
| GET/HEAD | `setting/service/{service}/edit` | `ServiceController@edit` | `admin.setting.service.edit` |

### Main Settings (Lookup Values)

| Method | URI | Controller@Method | Route Name |
|--------|-----|-------------------|------------|
| GET/HEAD | `setting/mainsetting` | `MainsettingController@index` | `admin.setting.mainsetting.index` |
| POST | `setting/mainsetting` | `MainsettingController@store` | `admin.setting.mainsetting.store` |
| GET/HEAD | `setting/mainsetting/create` | `MainsettingController@create` | `admin.setting.mainsetting.create` |
| PUT/PATCH | `setting/mainsetting/{mainsetting}` | `MainsettingController@update` | `admin.setting.mainsetting.update` |
| DELETE | `setting/mainsetting/{mainsetting}` | `MainsettingController@destroy` | `admin.setting.mainsetting.destroy` |
| DELETE\|GET | `setting/mainsetting/delete/{id}` | `MainsettingController@delete` | `admin.setting.mainsetting.delete` |

---

## Controllers

### MainController (`settings\MainController`)

Provides a single AJAX helper for dependent dropdowns.

#### `getDistricts(Request $request): JsonResponse`

- **Route:** POST `setting/getDistricts` → `admin.setting.getDistricts`
- **Inputs (optional query params):**
  - `city_id` (integer) — filter districts to a specific city
  - `search` (string) — filter by district name in the active locale using `LIKE`
- **Logic:**
  1. Build a `District::query()`.
  2. If `city_id` is provided, add `where('city_id', $cityId)`.
  3. If `search` is provided, add `where('name->' . app()->getLocale(), 'like', '%search%')`.
  4. Paginate with 10 per page.
- **Returns:** JSON-paginated collection of District records.

---

### CountryController

Uses the `ImageProcessing` trait. Upload folder: `countries`.

#### `index(Request $request)`

- **Route:** GET `setting/Country`
- **Inputs:** None (AJAX flag detected via `$request->ajax()`).
- **Logic:**
  1. If AJAX: query `Country::select('*')`, pipe through Yajra DataTables.
     - Adds a `data` column with avatar + name + phone code HTML.
     - Adds an `action` column with edit and delete buttons linking to `admin.setting.Country.edit` and `admin.setting.Country.destroy`.
     - Raw columns: `action`, `data`.
  2. If not AJAX: return `dashboard.admin.setting.Country.index` view.
- **Returns:** DataTables JSON or Blade view.

#### `create()`

- **Route:** GET `setting/Country/create`
- Empty — form is rendered inline on the index page via modal.

#### `store(Request $request)`

- **Route:** POST `setting/Country`
- **Inputs:** `name_en`, `name_ar`, `code_phone`, `image` (file), optional `id` (for upsert).
- **Validation** (private `countryValidator`):
  - `name_en`: required, string, max 255, unique on `countries.name->en` ignoring current id, excludes soft-deleted.
  - `name_ar`: required, string, max 255, unique on `countries.name->ar` ignoring current id, excludes soft-deleted.
  - `code_phone`: required, integer, min 1.
  - `image`: required (create) or nullable (update) — mimes: jpg, jpeg, png, webp.
- **Logic:**
  1. Run `countryValidator`; on failure return JSON 422 with errors.
  2. Build `$insert_data` from `$request->all()`.
  3. Set `name` as JSON: `['en' => name_en, 'ar' => name_ar]`.
  4. Normalize `code_phone` by stripping all non-digit characters then casting to int.
  5. If an image file is present, call `$this->saveImage($file, 'countries')` and store the returned path.
  6. Call `Country::updateOrCreate(['id' => $id], $insert_data)`.
- **Returns:** JSON 200 `{message: 'success'}` or JSON 500 on exception.

#### `edit(Request $request, $id)`

- **Route:** GET `setting/Country/{Country}/edit`
- **Logic:** Find Country by id; return JSON 200 `{one_data: $data}`.

#### `update(Request $request, $id)`

- **Route:** PUT/PATCH `setting/Country/{Country}`
- **Logic:** Runs `countryValidator` with the explicit id. On failure redirects back with errors. On success: find record, rebuild name JSON, normalize phone, optionally replace image, call `$data->update()`, flash toastr success, redirect to index.
- **Returns:** Redirect to `admin.setting.Country.index`.

#### `destroy($id)`

- **Route:** DELETE `setting/Country/{Country}`
- **Logic:** Find record, call `$this->deleteImage($delete_data->image)`, then `$delete_data->delete()`.
- **Returns:** JSON 200 on success, JSON 500 on exception.

---

### CityController

Uses the `ImageProcessing` trait. Upload folder: `city`.

#### `index(Request $request)`

- **Route:** GET `setting/city`
- **Logic:** AJAX → DataTables with `country` (relationship name), `data` (avatar HTML), `action` columns. Non-AJAX → view `dashboard.admin.setting.cities.index`.

#### `store(Request $request)`

- **Route:** POST `setting/city`
- **Inputs:** `name_en`, `name_ar`, `image` (optional file), `country_id`, optional `id`.
- **Validation:** name uniqueness on `city.name->en` / `city.name->ar` (soft-delete aware); `country_id` must exist in `countries`.
- **Logic:** Same upsert pattern as Country — build name JSON, optionally save image via `saveImage`, call `City::updateOrCreate`.
- **Returns:** JSON 200 / 422 / 500.

#### `edit(Request $request, $id)`

- **Route:** GET `setting/city/{city}/edit`
- **Logic:** `City::with('country')->find($id)` — includes related country. Returns JSON 200 `{one_data: $data}`.

#### `update(Request $request, $id)`

- **Route:** PUT/PATCH `setting/city/{city}`
- No explicit validation here; rebuilds name JSON, calls `$data->update()`, toastr, redirect.

#### `destroy($id)`

- **Route:** DELETE `setting/city/{city}`
- Calls `$this->deleteImage`, then soft-deletes. Returns JSON 200 / 500.

---

### DistrictController

No image handling. Uses `EditeDistrictRequest` form request for validation.

#### `index()`

- **Route:** GET `setting/district`
- **Logic:** Retrieves all districts and all cities (`District::all()`, `City::all()`), returns view `dashboard.admin.setting.district.show`.

#### `store(EditeDistrictRequest $request)`

- **Route:** POST `setting/district`
- **Inputs:** `name_ar`, `name_en`, `city_id` (validated by `EditeDistrictRequest`).
- **Logic:** Instantiate `District`, set `name` JSON, set `city_id`, save. Toastr success, redirect to index.

#### `update(EditeDistrictRequest $request, $id)`

- **Route:** PUT/PATCH `setting/district/{district}`
- **Logic:** `District::findOrFail($id)`, update name JSON and city_id, call `update()`. Toastr, redirect.

#### `delete($id)` / `destroy($id)`

- **Routes:** `DELETE|GET setting/district/delete/{id}` and `DELETE setting/district/{district}`
- **Logic:** `District::destroy($id)`. Toastr, redirect. `destroy` simply delegates to `delete`.

---

### NationalityController

No image handling.

#### `index(Request $request)`

- **Route:** GET `setting/nationality`
- **Logic:** AJAX → DataTables with `name` and `action` columns. Non-AJAX → view `dashboard.admin.setting.Nationality.index`.

#### `store(Request $request)`

- **Route:** POST `setting/nationality`
- **Inputs:** `name_en`, `name_ar`, optional `id`.
- **Validation:** Unique on `nationalities.name->en` and `nationalities.name->ar` ignoring current id.
- **Logic:** `Nationality::updateOrCreate(['id' => $id], $insert_data)` with `name` JSON.
- **Returns:** JSON 200 / 422 / 500.

#### `edit(Request $request, $id)`

- **Route:** GET `setting/nationality/{nationality}/edit`
- Returns JSON 200 `{one_data: $data}`.

#### `update(Request $request, $id)`

- **Route:** PUT/PATCH `setting/nationality/{nationality}`
- Finds record, rebuilds name JSON, calls `update()`. Returns JSON 200.

#### `destroy($id)`

- **Route:** DELETE `setting/nationality/{nationality}`
- Soft-deletes. Returns JSON 200 / 500.

---

### CategoryController

No image handling in `destroy`; `saveImage` is called in `store` but the trait is **not** explicitly `use`d in the class — this is a latent bug; image saving will fail unless a parent class provides the method.

#### `index(Request $request)`

- **Route:** GET `setting/Category`
- **Logic:** AJAX → DataTables including `data` column (avatar + name + parent name HTML) and `action` column. Also loads `$categroys = Category::whereNull('parent_id')->get()` for the non-AJAX view (parent options for the creation form). Returns view `dashboard.admin.setting.Category.index`.

#### `store(Request $request)`

- **Route:** POST `setting/Category`
- **Inputs:** `name_en`, `name_ar`, `parent_id` (optional), `image` (optional file), optional `id`.
- **Validation:** Unique name per locale (soft-delete aware); `parent_id` optional but must exist.
- **Logic:** Build name JSON; if image present call `saveImage`; `Category::updateOrCreate`.
- **Returns:** JSON 200 / 422 / 500.

#### `edit(Request $request, $id)`

- **Route:** GET `setting/Category/{Category}/edit`
- `Category::with('parent_data')->find($id)`. Returns JSON 200.

#### `update(Request $request, $id)`

- **Route:** PUT/PATCH `setting/Category/{Category}`
- Rebuild name JSON, call `update()`. Redirect to index.

#### `destroy($id)`

- **Route:** DELETE `setting/Category/{Category}`
- `Category::destroy($id)` — hard destroy via Eloquent's destroy (respects soft-delete model). Returns JSON 200.

---

### FilesController

No image handling. Manages document type label records.

#### `index(Request $request)`

- **Route:** GET `setting/files`
- **Logic:** AJAX → DataTables with `name` and `action` (dropdown menu HTML with edit/delete links). Non-AJAX → view `dashboard.admin.setting.files.index`.

#### `store(Request $request)`

- **Route:** POST `setting/files`
- **Inputs:** `name_en`, `name_ar`, optional `id`.
- **Validation:** Unique on `setting_files.name->en` and `name->ar` ignoring current id.
- **Logic:** `Files::updateOrCreate(['id' => $id], $insert_data)` with name JSON.
- **Returns:** JSON 200 / 422 / 500.

#### `edit(Request $request, $id)`

- **Route:** GET `setting/files/{files}/edit`
- Returns JSON 200 `{one_data: $data}`.

#### `update(Request $request, $id)`

- **Route:** PUT/PATCH `setting/files/{files}`
- Rebuilds name JSON, calls `update()`. Returns JSON 200.

#### `destroy($id)`

- **Route:** DELETE `setting/files/{files}`
- Soft-deletes. Returns JSON 200 / 500.

---

### TypeSettingController

Manages lookup group types (parent categories for `MainSetting` values).

#### `index()`

- **Route:** GET `setting/typesetting`
- **Logic:** `TypeSetting::all()`. Returns view `dashboard.admin.setting.typesetting.create` with all type records as `$obj`.

#### `store(type_settingRequest $request)`

- **Route:** POST `setting/typesetting`
- **Inputs:** `name_ar`, `name_en`, `code` (validated by `type_settingRequest`).
- **Logic:** Instantiate `TypeSetting`, set `title` JSON and `code`, save. Toastr, redirect to index.

#### `update(Request $request, $id)`

- **Route:** PUT/PATCH `setting/typesetting/{typesetting}`
- Find by id, update `title` JSON and `code`, call `update()`. Toastr, redirect.

#### `delete($id)` / `destroy($id)`

- **Routes:** `DELETE|GET setting/typesetting/delete/{id}` and `DELETE setting/typesetting/{typesetting}`
- **Logic:** Load `TypeSetting::with('maindata')->find($id)`, cascade delete all related `MainSetting` records via `$delete_data->maindata()->delete()`, then delete the type. Toastr, redirect.

---

### ServiceController

Uses `ImageProcessing` trait. Upload folder: `services`.

#### `index(Request $request)`

- **Route:** GET `setting/service`
- **Logic:** AJAX → DataTables with `data` (avatar + name) and `action` columns. Non-AJAX → view `dashboard.admin.setting.service.index`.

#### `store(Request $request)`

- **Route:** POST `setting/service`
- **Inputs:** `name_en`, `name_ar`, `type`, `code`, `image` (file — required on create, nullable on update), optional `id`.
- **Validation:** Unique name, type per locale (soft-delete aware); code required.
- **Logic:**
  1. Determine if this is create or update from `$request->filled('id')`.
  2. If update and image provided, delete the old image file first via `deleteImage`.
  3. Save new image via `saveImage` into `services/`.
  4. `Service::updateOrCreate(['id' => $id], $insertData)`.
- **Returns:** JSON 200 / 422 / 500.

#### `edit(string $id)`

- **Route:** GET `setting/service/{service}/edit`
- `Service::findOrFail($id)`. Returns JSON 200 `{one_data: $data}`.

#### `update(Request $request, string $id)`

- **Route:** PUT/PATCH `setting/service/{service}`
- Merges `id` into request then delegates to `store()`.

#### `destroy(string $id)`

- **Route:** DELETE `setting/service/{service}`
- `Service::findOrFail($id)`, delete image, soft-delete. Returns JSON 200 / 500.

---

### MainsettingController

Manages lookup values belonging to a `TypeSetting` group.

#### `index()`

- **Route:** GET `setting/mainsetting`
- **Logic:** `MainSetting::with('typedata')->get()` and `TypeSetting::get()`. Returns view `dashboard.admin.setting.mainsetting.create` with both collections.

#### `store(mainsettinRequest $request)`

- **Route:** POST `setting/mainsetting`
- **Inputs:** `title_ar`, `title_en`, `type_code`, `status` (validated by `mainsettinRequest`).
- **Logic:**
  1. Instantiate `MainSetting`.
  2. Set `title` JSON from `title_ar`/`title_en`.
  3. Resolve `type_id_fk` by looking up `TypeSetting::where('code', $type_code)->first()->id`.
  4. Set `type_id_fk`, `type_code`, `status`.
  5. Save. Toastr, redirect to index.

#### `update(mainsettinRequest $request, $id)`

- **Route:** PUT/PATCH `setting/mainsetting/{mainsetting}`
- Find by id, rebuild title JSON, resolve type FK again by code, update. Toastr, redirect.

#### `delete($id)` / `destroy($id)`

- **Routes:** `DELETE|GET setting/mainsetting/delete/{id}` and `DELETE setting/mainsetting/{mainsetting}`
- `MainSetting::destroy($id)`. Toastr, redirect. `destroy` delegates to `delete`.

---

### SubSettingController

This controller is registered in the codebase but is **not wired into any active route** in `admin.php`. Its `store` method contains incomplete code referencing an undefined variable `$im` and an undefined class `sub_setting`, indicating it is an abandoned draft. Do not rely on it.

---

## Models

### Country

| Property | Value |
|----------|-------|
| Table | `countries` |
| Traits | `HasFactory`, `HasTranslations`, `SoftDeletes` |
| Fillable | `name`, `image`, `code_phone` |
| Translatable | `name` (stored as JSON, keys: `en`, `ar`) |
| Cast | `code_phone` → `integer` |
| Appends | `imageUrl` |
| Timestamps | yes |

**Accessors:**
- `getImageUrlAttribute()` — calls the global helper `getDefultImage($this->image)` which returns the storage URL or a placeholder.

**Relationships:** none defined in model (no `City` reverse relation).

---

### City

| Property | Value |
|----------|-------|
| Table | `city` |
| Traits | `HasFactory`, `HasTranslations`, `SoftDeletes` |
| Fillable | `name`, `image`, `country_id` |
| Translatable | `name` |
| Appends | `imageUrl` |

**Relationships:**
```php
public function country(): BelongsTo
// belongsTo(Country::class, 'country_id')
```

---

### District

| Property | Value |
|----------|-------|
| Table | `district` |
| Traits | `HasFactory`, `HasTranslations`, `SoftDeletes` |
| Fillable | `name`, `city_id`, `status` |
| Translatable | `name` |

**Relationships:**
```php
public function city(): BelongsTo
// belongsTo(City::class)
```

---

### Nationality

| Property | Value |
|----------|-------|
| Table | `nationalities` |
| Traits | `SoftDeletes`, `HasTranslations`, `HasFactory` |
| Fillable | `name` |
| Translatable | `name` |

No relationships.

---

### Category

| Property | Value |
|----------|-------|
| Table | `categories` |
| Traits | `HasFactory`, `HasTranslations`, `SoftDeletes` |
| Fillable | `name`, `created_by`, `image`, `parent_id`, `type`, `external_id` |
| Translatable | `name` |
| Appends | `image_path` |

**Accessors:**
- `getImagePathAttribute()` — `getDefultImage($this->image)`.

**Relationships:**
```php
public function parent_data(): BelongsTo
// belongsTo(Category::class, 'parent_id')->withDefault()

public function children_data(): HasMany
// hasMany(Category::class, 'parent_id')

public function created_by(): BelongsTo
// belongsTo(Admin::class, 'created_by')
```

---

### Files

| Property | Value |
|----------|-------|
| Table | `setting_files` |
| Traits | `SoftDeletes`, `HasTranslations`, `HasFactory` |
| Fillable | `name` |
| Translatable | `name` |

No relationships.

---

### TypeSetting

| Property | Value |
|----------|-------|
| Table | `type_setting` |
| Traits | `SoftDeletes`, `HasTranslations`, `HasFactory` |
| Fillable | `title`, `code` |
| Translatable | `title` |
| Timestamps | yes |

**Relationships:**
```php
public function maindata(): HasMany
// hasMany(MainSetting::class, 'type_id_fk')
```

---

### MainSetting

| Property | Value |
|----------|-------|
| Table | `main_setting` |
| Traits | `HasFactory`, `SoftDeletes`, `HasTranslations` |
| Fillable | `title`, `type_id_fk`, `type_code`, `status` |
| Translatable | `title` |
| Timestamps | yes |

**Cache behavior:** The model's `boot()` method hooks `saved` and `deleted` events to call `Cache::forget('main_settings_all')`.

**Static helpers:**
```php
// Returns all active records; result is cached forever under 'main_settings_all'
public static function getAllCached(): Collection

// Returns translated titles for the given array of IDs (from cache)
public static function getTitlesByIds(array $ids): Collection
```

**Relationships:**
```php
public function typedata(): BelongsTo
// belongsTo(TypeSetting::class, 'type_id_fk')
```

---

### Service

| Property | Value |
|----------|-------|
| Table | `services` |
| Traits | `HasFactory`, `HasTranslations`, `SoftDeletes` |
| Fillable | `name`, `image`, `type`, `code` |
| Translatable | `name` |
| Appends | `imageUrl` |

**Accessors:**
- `getImageUrlAttribute()` — `getDefultImage($this->image)`.

---

## File / Image Upload Handling

Two traits exist; which one a controller uses depends on which is imported via `use`.

### Trait: `ImageProcessing` (`app/Traits/ImageProcessing.php`)

Used by: `CountryController`, `CityController`, `ServiceController`.

**Storage driver:** Custom `images` disk. Files land at `storage/app/images/{folder}/`.

Key methods:

```php
// Save image at original quality; returns relative path e.g. 'countries/abc123.jpg'
public function saveImage($image, $folder = null): string

// Save image resized to $width×$height keeping aspect ratio
public function aspect4resize($image, $width, $height, $folder): string

// Save full image + up to three thumbnail sizes (256×144, 426×240, 640×360)
public function saveImageAndThumbnail($file, $folder, $thumb = false): array

// Delete image file from the images disk
public function deleteImage($filePath): void
```

`saveImage` flow:
1. Try to create an Intervention Image instance via `Image::make()`.
2. If Intervention is unavailable, fall back to `storeOriginalUploadedFile()` which does a plain `copy()`.
3. Otherwise: determine extension from MIME, generate a random 8-char + timestamp filename, ensure the folder exists (`checkFolder` uses `File::makeDirectory`), and save via Intervention's `$img->save()`.

Filename generation: `Str::random(8) . time() . '.' . $extension`.

### Trait: `ImageUploadTrait` (`app/Traits/ImageUploadTrait.php`)

Used by: `UsersController` (admin users) and any controller that imports it explicitly.

**Storage driver:** `public_path()` — files land in `public/{folder}/`.

Key method:

```php
public function uploadImage(
    $image,
    string $folder = 'uploads',
    int $width = 1200,
    int $height = null,
    string $format = 'webp',
    int $quality = 85,
    bool $thumbnail = false,
    int $thumbWidth = 300,
    int $thumbHeight = 300,
    string $oldImage = null
): array  // returns ['image' => path, 'thumbnail' => path|null]
```

`saveImage()` is a convenience wrapper calling `uploadImage` with hardcoded defaults: `600×600`, format `webp`, quality `90`, thumbnail `true` at `150×150`.

Thumbnail logic: uses `fitCrop()` (scale up to cover, then centre-crop to exact size). Main image uses `resizeAspect()` (scale down only, never upscale).

Filename generation: `Str::uuid() . '.' . $format` → e.g. `f47ac10b-58cc-4372-a567-0e02b2c3d479.webp`.

Old-image deletion: if `$oldImage` is provided, the file is deleted from `public_path($oldImage)` before saving the new one.

**Important difference from `ImageProcessing`:** `ImageUploadTrait` stores files under `public/` (web-accessible directly), while `ImageProcessing` stores under `storage/app/images/` (requires a symlink or custom disk config to serve).
