## Getting Started
### First Steps
1. **Installation**: Run `composer require bosnadev/database` in your project root. This adds PostgreSQL-specific Eloquent extensions.
2. **Service Provider**: Register `Bosnadev\Database\DatabaseServiceProvider` in `config/app.php` under `providers`.
3. **Database Configuration**: Ensure your `.env` has PostgreSQL configured (e.g., `DB_CONNECTION=pgsql`).
4. **First Use Case**: Test with a model using extended types:
```php
use Bosnadev\Database\Eloquent\Model;
use Bosnadev\Database\Eloquent\Relations\HasHStore;
use Bosnadev\Database\Eloquent\Relations\HasUUID;
class Product extends Model
{
use HasHStore, HasUUID;
protected $hstoreKey = 'metadata';
protected $keyType = 'uuid';
}
php artisan migrate to confirm migrations work with new types.src/Eloquent/Model.php: Base model traits for extended types.src/Eloquent/Relations/: Custom relation classes (e.g., HasHStore, HasUUID).src/PostgresManager.php: Core PostgreSQL-specific logic (e.g., type casting).$product = new Product();
$product->metadata = ['price' => '19.99', 'tags' => 'premium,limited'];
$product->save();
// Get products with 'premium' tag
$products = Product::whereHStoreContains('metadata', 'tags', 'premium')->get();
Schema::create('products', function (Blueprint $table) {
$table->hstore('metadata'); // Custom column type
});
class Product extends Model
{
protected $keyType = 'string';
protected $primaryKey = 'uuid';
}
Schema::create('products', function (Blueprint $table) {
$table->uuid('uuid')->primary();
});
$product = Product::where('uuid', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')->first();
$location = new Location();
$location->point = 'POINT(10 20)';
$location->polygon = 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))';
$location->save();
// Find locations within a bounding box
$locations = Location::where('point', '&&', 'BOX(5 5, 15 15)')->get();
Schema::create('locations', function (Blueprint $table) {
$table->point('point');
$table->polygon('polygon');
});
use Bosnadev\Database\Eloquent\Casts\PostgresCast;
class CustomCast extends PostgresCast
{
public function get()
{
return json_decode($this->model->{$this->attribute}, true);
}
public function set($value)
{
return json_encode($value);
}
}
class Product extends Model
{
protected $casts = [
'custom_data' => CustomCast::class,
];
}
Seeding:
Use DB::statement() for complex PostgreSQL operations in seeds:
DB::statement("INSERT INTO locations (point) VALUES ('POINT(1 1)')");
Raw Queries: Leverage PostgreSQL-specific functions:
$results = DB::select("SELECT ST_Distance(point, 'POINT(2 2)') FROM locations");
Testing:
Use DatabaseMigrations and RefreshDatabase traits with PostgreSQL:
use Bosnadev\Database\DatabaseServiceProvider;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ProductTest extends TestCase
{
use DatabaseMigrations;
public function testHStore()
{
// Test logic here
}
}
Performance:
hstore columns, consider indexing:
Schema::table('products', function (Blueprint $table) {
$table->hstore('metadata')->index();
});
->select() to limit columns for geometric queries to reduce overhead.Type Mismatches:
$casts or traits. For example, if using HasUUID, the column must be defined as uuid() in migrations, not string().php artisan migrate:fresh if types are misconfigured.HStore Serialization:
hstore data is stored as text. Unexpected serialization (e.g., json_encode on arrays) can corrupt data.$model->metadata['key'] = 'value') instead of raw JSON.PostGIS Dependencies:
CREATE EXTENSION postgis;
pgAdmin to enable PostGIS.UUID Conflicts:
HasUUID trait handles generation, but custom logic may bypass this.Str::uuid()->toString() for manual generation or validate uniqueness in migrations.Case Sensitivity:
point vs Point).Query Logs: Enable query logging to debug complex PostgreSQL queries:
DB::enableQueryLog();
$results = Product::whereHStoreContains('metadata', 'tags', 'premium')->get();
dd(DB::getQueryLog());
Raw SQL:
For geometric queries, test raw SQL in psql or a PostgreSQL client first:
SELECT ST_Distance(point, 'POINT(2 2)') FROM locations;
Schema Dumps:
Use php artisan schema:dump to inspect generated migrations and identify mismatches.
Connection Overrides:
The package assumes the default pgsql connection. Override in .env if needed:
DB_CONNECTION=pgsql_custom
Then configure config/database.php accordingly.
Timezone Handling:
PostgreSQL timestamps respect the database’s timezone. Set this in .env:
DB_TIMEZONE=UTC
Custom Traits:
Extend existing traits (e.g., HasHStore) to add domain-specific logic:
trait HasCustomHStore extends HasHStore
{
public function scopeWithNestedHStore($query, $key, $nestedKey, $value)
{
return $query->whereRaw("metadata->>'$key'->>'$nestedKey' = ?", [$value]);
}
}
Query Builder Extensions: Add custom methods to the query builder by publishing the package’s config:
php artisan vendor:publish --provider="Bosnadev\Database\DatabaseServiceProvider"
Then extend app/Extensions/QueryBuilder.php.
Event Listeners: Listen for model events to transform data before/after saving:
Product::saved(function ($product) {
if ($product->wasChanged('metadata')) {
// Log or process hstore changes
}
});
Testing Helpers: Create custom test helpers for PostgreSQL-specific assertions:
function assertHStoreContains($model, $key, $value)
{
$this->assertTrue($model
How can I help you explore Laravel packages today?