Skip to content

Eloquent: Getting Started

Introduction

Doppar features its own powerful data interaction tool, Eloquent, an object-relational mapper (ORM), which simplifies and enhances the way you work with your database. With ORM, every database table is linked to a dedicated "Data Model" that serves as your gateway to managing table data seamlessly. Beyond just fetching records, Doppar Data Mapper empowers you to effortlessly insert, update, and delete records, making database interactions intuitive and efficient. Whether you're building complex queries or handling simple data operations, ORM ensures a smooth and enjoyable experience, tailored to streamline your development workflow.

Creating Model Classes

To get started, let's create an Eloquent model. Models typically live in the app\Models directory and extend the Phaseolies\Database\Eloquent\Model class. You may use the make:model Pool command to generate a new model:

bash
php pool make:model Post

This command will generate a new model inside App\Models directory. Models generated by the make:model command will be placed in the app/Models directory. Let's see a basic model class.

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class Post extends Model
{
    //
}

Model Properties

Before diving into Doppar's data management capabilities, it’s important to familiarize yourself with some key model properties that shape how your data is handled. Doppar offers the flexibility to customize these properties to suit your specific needs. Key properties include $pageSize, which controls the number of records displayed per page; $primaryKey, which defines the unique identifier for your table; $table, which specifies the database table associated with the model; $creatable, which determines whether new records can be added; and $unexposable and $timeStamps, which allows you to hide sensitive or irrelevant data from being exposed and handle datetime columns. With Doppar, you have full control to tweak these properties, ensuring your data interactions are both efficient and secure. Let's see the User model as for example.

$primaryKey

Specifies the column name that serves as the unique identifier for the table. By default, this is set to 'id', but it can be customized if your table uses a different primary key.

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * The primary key associated with the table.
     *
     * @var int
     */
    protected $primaryKey = 'id';
}

$table

Specifies the database table associated with this model

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'posts';
}

$creatable

Specifies which attributes can be mass-assigned when creating or updating records.This helps prevent mass assignment vulnerabilities by explicitly defining safe fields. Only the attributes listed here can be set in bulk operations.

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Creatable Attributes
     *
     * @var array
     */
    protected $creatable = ['title', 'status', 'description'];
}

$unexposable

Specifies which attributes should be hidden when the model is converted to an array or JSON or Collection. This is particularly useful for hiding sensitive information, such as passwords, from being exposed in API responses or other outputs.

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Unexposable Attributes
     *
     * @var array
     */
    protected $unexposable = ['password'];
}

$timeStamps

Indicates whether the model should maintain timest(created_at and updated_at fields.).

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    protected $timeStamps = true;
}

Query Using ORM

Retrieving Models

Once you have created a model and its associated database table, you are ready to start retrieving data from your database. You can think of each Eloquent model as a powerful query builder allowing you to fluently query the database table associated with the model. The model's all method will retrieve all of the records from the model's associated database table:

php
<?php

use App\Models\Post;

$posts = Post::all();

// Or you can use direct get() method
$posts = Post::get();

foreach (Post::all() as $post) {
    echo $post->title;
}

Expending Queries

The all method in Eloquent fetches every record from a model’s corresponding table. However, because Eloquent models double as query builders, you can refine queries using conditions and retrieve only the matching results by calling the get method.

php
$posts = Post::query()
    ->where('status', '=', true)
    ->orderBy('title')
    ->limit(10)
    ->get();

You can write query with multiple where condition by chaining multiple where with the queries

php
Post::query()
    ->where('title', '=', 'Sincere Littel')
    ->where('status', '=', 1)
    ->first();

Collections

As observed, Eloquent methods such as all and get are designed to fetch multiple records from the database. Rather than returning a standard PHP array, these methods yield an instance of Phaseolies\Support\Collection, providing a rich set of tools to work with the retrieved models efficiently.

If you want to convert collection to array, you can use toArray() method.

php
$posts = Post::query()
    ->where('status', '=', true)
    ->orderBy('title')
    ->limit(10)
    ->get()
    ->toArray();

Fetch the First Records

To fetch the first records you can use first() function as like below

php
Post::query()->first();

Actually this above query returns the Post model object so you can directly fetch its attributes like

php
Post::query()->first()?->title ?? 'defult';

GroupBy and OrderBy

You can also use groupBy() and orderBy() to handle your records like

php
User::query()->orderBy('id', 'desc')->groupBy('name')->get();

toSql()

The toSql() method in Eloquent is a useful tool when you want to inspect the raw SQL query that would be executed for a given Eloquent query. Instead of retrieving the results from the database, toSql() returns the SQL statement as a string, allowing you to debug or log the query before it's run. This is especially helpful during development when you need to understand how your query builder chain is being translated into SQL.

Here's an example:

php
User::query()
    ->where('status', '=', 'active')
    ->toSql();

This will output:

sql
select * from `users` where `status` = ?

Using toSql() helps you optimize and troubleshoot queries by giving insight into what Doppar is sending to the database under the hood.

find()

The find() method in Eloquent is used to retrieve a single record from the database by its primary key. It offers a quick and efficient way to look up a specific model instance without having to write a full where clause. If a matching record is found, an instance of the model is returned; otherwise, null is returned.

Here’s a basic example:

php
$user = User::find(1);

In this case, Eloquent returns a collection of User instances corresponding to the given IDs. find() is ideal when you know the exact primary key(s) you're looking for and want a concise way to retrieve the corresponding record(s).

This is the sort version of this query

php
$user = User::query()->where('id', '=', 1)->first();

count()

To see how many records your table has, you can use count() function as like

php
User::count();
User::query()->orderBy('id', 'desc')->groupBy('name')->count();
User::query()->where('active', '=', true)->count();

Newest and Oldest Records

Doppar makes it convenient to retrieve the most recent or earliest records from a database using the newest() and oldest() query methods. These methods sort the results based on a specified column, with id being the default field if none is provided.

To fetch the latest records (in descending order):

php
User::query()->newest()->get();

This retrieves the most recently added users based on the id column by default. If you want to sort by a different column, you can pass it as an argument:

php
User::query()->newest('name')->get();

Similarly, to retrieve the oldest records (in ascending order):

php
User::query()->oldest()->get();

Again, you can specify a custom column to determine what "oldest" means in your context:

php
User::query()->oldest('id')->get();

These methods offer a clean and readable way to sort query results by time or any relevant field, enhancing code clarity while maintaining flexibility.

Selecting Specific Columns

In many cases, you may not need to retrieve every column from a table—especially when working with large datasets. Eloquent's select() method allows you to specify exactly which columns you want to fetch, helping optimize performance and reduce memory usage.

Here are a few examples:

php
// Selecting specific columns using an array
User::query()->select(['name', 'email'])->get();

// Selecting specific columns using multiple arguments
User::query()->select('name', 'email')->get();

Both of these will return only the name and email fields for each user, excluding all other columns.

You can also use select() in more advanced queries, such as when grouping results:

php
Post::query()
    ->select('title', 'COUNT(*) as count')
    ->groupBy('category_id')
    ->get();

Using select() helps tailor your queries to fetch only the data you actually need, improving both efficiency and clarity in your code.

selectRaw()

The selectRaw() method in Doppar's query builder provides a powerful way to include raw SQL expressions in your queries. It's especially useful when you need to perform calculations, use SQL functions, or include complex expressions that go beyond Eloquent's default capabilities.

Basic Usage

You can use selectRaw() to write a custom SQL expression directly in the SELECT clause. For example, to count the total number of orders:

php
use App\Models\Order;

$total = Order::query()
    ->selectRaw('COUNT(*) as order_count')
    ->first();

echo $total->order_count;

This will execute a query similar to:

sql
SELECT COUNT(*) as order_count FROM orders;

Calculated Columns

You can also perform calculations using existing fields in the database. For instance, to calculate the total value of each order:

php
$orders = Order::query()
    ->selectRaw('price * quantity as total_value')
    ->get();

foreach ($orders as $order) {
    echo $order->total_value;
}

This calculates a total_value field on the fly for each record using the price and quantity columns.

Chaining Multiple selectRaw() Calls

You can chain multiple selectRaw() calls to build a SELECT clause that includes several raw expressions. Each call adds to the final SQL:

php
$orders = Order::query()
    ->selectRaw('SUM(price) as total_sales')
    ->selectRaw('AVG(quantity) as average_quantity')
    ->selectRaw('MAX(price) as highest_price')
    ->first();

This returns an object with aggregated values such as total sales, average quantity, and the highest price.

⚠️ Security Tip: When using raw expressions, especially with user input, always bind parameters or sanitize values to protect against SQL injection.

Using Bindings with selectRaw()

Scenario: You're running an e-commerce platform. You store orders in an orders table with these columns:

  • price: base price of an item
  • category_id: the category the product belongs to
  • status: whether the order is completed (true) or not
  • created_at: timestamp of the order

You want to:

  • Get the maximum order value after tax (e.g. 8%) and after VAT (e.g. 20%)
  • Only consider completed orders
  • Group results by category

Now assume "For each product category, what's the highest order value including tax and VAT, from completed orders?"

php
$orders = Order::query()
    ->selectRaw('MAX(price * ?) AS total_with_tax', [1.08]) // Apply 8% tax
    ->selectRaw('MAX(price * ?) AS total_with_vat', [1.20]) // Apply 20% VAT
    ->where('status', '=', true)
    ->groupByRaw('category_id')
    ->get();

Example output

category_idtotal_with_taxtotal_with_vat
1108.00120.00
2216.00240.00
3162.00180.00

Why Use selectRaw Here?

  • You need to run math operations (price * taxRate) directly in SQL.
  • You want to bind the tax rate dynamically (?) to avoid hardcoding values.
  • You’re leveraging SQL’s aggregate function (MAX()) for reporting.

Bindings are passed as an array to selectRaw() and must match the number of ? placeholders in the SQL expression.

exists()

To check whether a specific row exists in your database, you can use the exists() function. This method returns a boolean value (true or false) based on whether the specified condition matches any records. Here's an example:

php
// Returns `true` if a matching row exists, otherwise `false`.
User::query()->where('id', '=', 1)->exists();

whereIn()

The whereIn() method filters records where a column's value matches any value in the given array.

php
User::query()->whereIn('id', [1, 2, 4])->get();

This retrieves all users with id values of 1, 2, or 4.

orWhereIn()

The orWhereIn() method filters records where a column's value optionally matches any value in the given array.

php
User::query()->orWhereIn('id', [1, 2, 4])->get();

whereBetween()

The whereBetween() method in Doppar lets you filter records where a column’s value falls within a specified range. It’s particularly useful for working with numeric values or date/time columns, such as filtering records created within a certain time frame.

Example: Filter Users by Date Range

php
User::query()
    ->whereBetween('created_at', ['2025-02-29', '2025-04-29'])
    ->get();

This query will retrieve all users whose created_at timestamp falls between February 29, 2025 and April 29, 2025, inclusive.

You can use whereBetween() with any comparable column — such as prices, IDs, scores, or dates — to make your queries more expressive and efficient.

php
User::query()->whereBetween('id', [1, 10])->get();

This query retrieves all user records where the id is between 1 and 10, inclusive. That means it will return users with id values of 1, 2, 3, ..., 10.

whereNotBetween()

The whereNotBetween() method allows you to filter records where a given column's value do not falls within a specified range. This is commonly used for date or numerical ranges.

php
User::query()
    ->whereNotBetween('created_at', ['2025-02-29', '2025-04-29'])
    ->get();

orWhereBetween()

Doppar allows you to construct expressive, flexible queries using methods like orWhereBetween(). This method is particularly useful when you want to retrieve records that match either a specific condition or fall within a certain range.

Example: Combine exact match with a range

php
Post::query()
    ->where('status', ,'=', 'published')
    ->orWhereBetween('views', [100, 500])
    ->get();

This will return all posts that are either published or have between 100 and 500 views, inclusive.

orWhereNotBetween()

Similarly, orWhereNotBetween() is used when you want to retrieve records that match a condition or fall outside a given range. This adds more control to your filtering logic.

php
Post::query()
    ->where('status', ,'=', 'published')
    ->orWhereNotBetween('views', [100, 500])
    ->get();

This query retrieves posts that are either published or have a view count less than 100 or greater than 500.

whereNull()

In Doppar, the whereNull() method is used to filter records where a specific column has no value—i.e., it contains NULL. This is useful when you're identifying incomplete or pending data.

Example: Find Posts Without a Created Date

php
Post::query()
    ->whereNull('created_at')
    ->get();

This query retrieves all posts where the created_at field is NULL, which might indicate drafts or records that haven't been finalized yet.

whereNotNull()

Conversely, whereNotNull() filters records where a given column does contain a value. It's ideal for fetching entries that are already completed or published.

Example: Fetch Published Posts

php
Post::query()
    ->whereNotNull('published_at')
    ->get();

This retrieves all posts that have a value in the published_at field, meaning they’ve been published.

orWhereNull()

The orWhereNull() method in Eloquent is used to add an OR condition to the query, checking if a column is NULL. In your example

php
Post::query()
    ->where('status', '=', 'draft')
    ->orWhereNull('reviewed_at')
    ->get();

orWhereNotNull()

The orWhereNotNull() method in Eloquent is used to add an OR condition to the query, checking if a column is not NULL. In your example:

php
Post::query()
    ->where('status', '=', 'draft')
    ->orWhereNotNull('reviewed_at')
    ->get();

pluck()

The pluck() method in Doppar is a convenient way to extract the values of a single column from your query results. Instead of returning full model instances, pluck() pulls out just the values from the specified field—perfect for generating simple lists.

Example: Get All Post Titles

php
Post::query()->pluck('title');

This will return a collection of all titles from the posts table, such as:

php
[
    "How to Use Doppar",
    "Getting Started with PHP",
    "Understanding Eloquent Queries"
]

useRaw()

The useRaw() method allows you to run raw SQL queries with parameter bindings, which helps prevent SQL injection by safely binding parameters to the query.

php
User::query()->useRaw(
    'SELECT * FROM user WHERE created_at > ? AND status = ?',
    ['2023-01-01', 'active']
);

useRaw runs a custom SQL query that retrieves users created after January 1, 2023, and with an active status. The values 2023-01-01 and active are securely bound to the query to prevent SQL injection.

Query Filter Using match()

The match() method in Doppar offers a clean and expressive way to apply dynamic, reusable filters to your queries. It supports both simple key-value pairs and more complex conditions using callbacks, making it ideal for building powerful, customizable queries without cluttering your controller or model logic.

Basic Filtering

php
Post::match([
    'id' => 1,
    'user_id' => 1
])->get();

Applies standard where clauses for each key-value pair.

Advanced Filtering with Callback

php
Post::match(function ($query) {
    $query->where('views', '>', 100)
          ->whereBetween('created_at', ['2023-01-01', '2023-12-31']);
})->get();

Allows complex conditions inside a closure for more flexible logic.

Filter Using Request Data

php
Post::match($request->only(['title', 'user_id']))->paginate();

Automatically filters based on available request fields—great for search forms or filters.

Combining Simple and Complex Filters

php
Post::match([
    'user_id' => [1, 2, 3],
    'created_at' => null,
    'active' => function ($query) {
        $query->where('active', 1)
              ->orWhere('legacy', 1);
    }
])
->orderBy('created_at', 'desc')
->get();

The match() method enhances query readability and reduces repetitive condition-building, especially in service layers or controllers.

if() for Conditional Query Execution

In Doppar, the if() method provides a powerful and elegant way to conditionally add query constraints based on a given condition. This allows you to build more flexible and dynamic queries, improving code readability and reducing unnecessary complexity.

Doppar ORM's if() method allows you to conditionally add query constraints based on a given condition. If the condition evaluates to true, the corresponding query modification is applied; otherwise, it is skipped.

Basic Usage of if()

php
Post::query()
    ->if($request->has('date_range'), function($query) use ($request) {
        $query->whereBetween('created_at', [
            $request->input('date_range.start'),
            $request->input('date_range.end')
        ]);
    })
    ->get();

In this example, the if() method checks if the date_range is present in the request. If it is, the query will filter the posts based on the provided start and end dates. If not, the whereBetween() condition is skipped.

Conditional Query Modifications with a Default Case

You can also provide a default case to be applied when the condition is false or not provided:

php
Post::query()
    ->if($request->input('search'),
        fn($q) => $q->where('title', 'LIKE', "%{$request->search}%"), // If search is provided, filter by title
        fn($q) => $q->where('is_featured', '=', true) // If no search is provided, filter by featured status
    )
    ->get();

In this example:

  • If a search parameter is provided, the query will search posts by title.
  • If no search parameter is found, it defaults to filtering posts where is_featured is true.

How if() Works with Different Conditions

Will execute when the condition is truthy:

php
Post::query()
    ->if(true, fn($q) => $q->where('active', '=', true)) // Executes because true
    ->if('text', fn($q) => $q->where('title', '=', 'text')) // Executes because 'text' is truthy
    ->if(1, fn($q) => $q->where('views', '=', 1)) // Executes because 1 is truthy
    ->if([1], fn($q) => $q->whereIn('id', '=', [1])) // Executes because [1] is truthy
    ->get();

Will NOT execute when the condition is falsy:

php
Post::query()
    ->if(false, fn($q) => $q->where('active', '=', false)) // Does not execute because false
    ->if(0, fn($q) => $q->where('views', '=', 0)) // Does not execute because 0 is falsy
    ->if('', fn($q) => $q->where('title', '=', '')) // Does not execute because empty string is falsy
    ->if(null, fn($q) => $q->where('deleted_at', '=', null)) // Does not execute because null is falsy
    ->if([], fn($q) => $q->whereIn('id', '=',  [])) // Does not execute because empty array is falsy
    ->get();

This makes the if() method powerful for dynamically building queries based on various conditions. It allows for more concise and flexible query building without having to manually check each condition before applying the relevant query changes.

Insertion and Update

Inserts

Of course, when using Doppar, working with data isn't limited to just retrieving records — inserting new entries is just as important. Fortunately, Doppar makes this process straightforward. To add a new record to the database, you simply create a new instance of the model, assign values to its attributes, and then call the save() method:

php
use App\Models\User;

$user = new User();
$user->name = 'Mahedi Hasan';
$user->email = 'mahedi@doppar.com';
$user->save();

This inserts a new row into the users table with the specified name and email. The save() method takes care of generating and executing the appropriate SQL insert query behind the scenes, making data insertion clean and intuitive.

Alternatively, you may use the create method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the create method:

php
use App\Models\User;

User::create([
    'name' => 'Doppar'
]);

Updates

The save() method in Doppar isn't just for inserting new records—it can also be used to update existing ones. To update a record, first retrieve the model instance, modify the attributes you want to change, and then call save(). Doppar will handle generating the appropriate UPDATE statement. Additionally, the updated_at timestamp will be refreshed automatically, so you don't need to set it manually:

php
use App\Models\Flight;

$flight = Flight::find(1);
$flight->name = 'Dhaka to Mumbai';
$flight->save();

Occasionally, you may need to update an existing model. The updateOrCreate() method, the updateOrCreate() method persists the model, so there's no need to manually call the save method.

The updateOrCreate() method is used to either update an existing record or create a new one if no matching record is found. It simplifies handling scenarios where you need to ensure a record exists with specific attributes while updating other fields.

php
$user = User::updateOrCreate(
    ['email' => 'howdy@doppar.com'], // attributes to match
    ['name' => 'Doppar'] // values to update/create
);

Mass Updates

Updates can also be performed against models that match a given query. In this example, that are active will be marked as delayed:

php

User::query()
    ->where('country', '=', 'bd')
    ->update([
        'code' =>  '+880'
    ]);

Updating Data with fill()

In Doppar, you can streamline the process of updating multiple attributes by using the fill() method. This method allows you to pass an array of key-value pairs representing the fields you want to update. After filling the model with new data, you simply call save() to persist the changes:

php
$user = User::find(1);
$user->fill([
    'name' => 'John',
    'email' => 'updated@doppar.com',
]);
$user->save();

Bulk Insert Using saveMany()

When you need to insert multiple records at once, Doppar provides the saveMany() method, which simplifies batch inserts. Instead of saving each record individually, you can pass an array of data, and Doppar will insert them all in a single operation.

Here’s an example of how you can use saveMany() to insert multiple users:

php
User::saveMany([
    ['name' => 'John', 'email' => 'john@example.com', 'password' => bcrypt('password')],
    ['name' => 'Jane', 'email' => 'jane@example.com', 'password' => bcrypt('password')],
    ['name' => 'Bob', 'email' => 'bob@example.com', 'password' => bcrypt('password')]
]);

This inserts all three users into the users table in one batch, making the process more efficient than inserting them one by one.

Chunk Size for Large Datasets

If you are working with a large dataset, inserting all records at once might lead to performance issues or memory limitations. In such cases, you can specify a chunk size as the second parameter to saveMany() to batch the inserts into smaller chunks. For example:

php
User::saveMany([
    ['name' => 'John', 'email' => 'john@example.com', 'password' => bcrypt('password')],
    ['name' => 'Jane', 'email' => 'jane@example.com', 'password' => bcrypt('password')],
    ['name' => 'Bob', 'email' => 'bob@example.com', 'password' => bcrypt('password')]
], 1000);

This will insert the records in chunks of 1000, helping prevent memory overflow and improving performance when dealing with a large volume of data.

Using saveMany() is a great way to handle bulk inserts in Doppar, ensuring your application remains efficient even with large datasets.

Mass Assignment

You may use the create method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the method:

php
use App\Models\User;

$user = User::create([
    'name' => 'Nure',
]);

However, before using the create method, you will need to specify a $creatable property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default.

php
<?php

namespace App\Models;

use Phaseolies\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $creatable = ['name'];
}

So, to get started, you should define which model attributes you want to make mass assignable. You may do this using the $creatable property on the model. For example, let's make the name attribute of our User model mass assignable:

Deleting Models

To delete a model, you may call the delete method on the model instance:

php
User::find(1)->delete();

Deleting an Existing Model by its Primary Key

In Doppar, if you already know the primary key(s) of the records you want to delete, there's no need to retrieve the models first. Instead, you can call the purge method directly. This method efficiently deletes records by their primary key and supports various input formats, including single IDs, multiple IDs, and arrays

php
User::purge(1);
User::purge(1, 2, 3);
User::purge([1, 2, 3]);

Deleting Models Using Queries

Of course, Doppar also allows you to perform bulk deletions by building a query that targets specific records. For instance, you can delete all users that are marked as inactive in a single operation. Just like with mass updates, these mass deletions are executed directly in the database.

php
User::query()
    ->where('active', '=', false)
    ->delete();

Aggregation

The Doppar ORM provides a rich set of aggregation functions that allow you to perform statistical and summary operations directly through your model queries. These methods help extract meaningful insights from your data without writing raw SQL, making your code clean, expressive, and efficient.

Total Sum of a Column

To calculate the sum of all values in a specific column (e.g., total views across all posts):

php
Post::query()->sum('views');

Average Value of a Column

To compute the average value of a column (e.g., average number of views):

php
Post::query()->avg('views');

Maximum Value in a Column

To get the highest value in a column (e.g., the most expensive product):

php
Product::query()->max('price');

Minimum Value in a Column

To get the lowest value in a column (e.g., cheapest product):

php
Product::query()->min('price');

Standard Deviation Calculation

To compute the standard deviation of values in a column (useful for measuring variability):

php
Product::query()->stdDev('price');

Variance Calculation

To calculate the variance of values (a squared measure of data spread):

php
Product::query()->variance('price');

Multiple Aggregations in One Query

To retrieve count, average, min, and max in a single grouped query:

php
Product::query()
    ->select([
        'COUNT(*) as count',
        'AVG(price) as avg_price',
        'MIN(price) as min_price',
        'MAX(price) as max_price'
    ])
    ->groupBy('variants')
    ->first();

This groups data by variants and returns aggregated stats for each group.

Fetching Distinct Rows

To get unique values from a column (e.g., all unique user IDs who posted):

php
Post::query()->distinct('user_id');

Calculating Conditional Sum

To sum values under a specific condition (e.g., sales where title is "maiores"):

php
Product::query()
    ->where('title', '=', 'maiores')
    ->sum('price');

Total Sales by Category

To aggregate sales per category by multiplying price and quantity:

php
return Product::query()
    ->select(['category_id', 'SUM(price * quantity) as total_sales'])
    ->groupBy('category_id')
    ->get();

Column Modification: increment() & decrement()

Incrementing a Column

To increase the value of a numeric column (e.g., views):

php
$post = Post::find(1);
$post->increment('views'); // Increments by 1 by default
$post->increment('views', 10); // Increments by 10

You can also update additional fields during the increment:

php
$post->increment('views', 1, [
    'updated_at' => date('Y-m-d H:i:s'),
    'modified_by' => Auth::id()
]);

Decrementing a Column

To decrease the value of a numeric column:

php
$post = Post::find(1);
$post->decrement('views'); // Decrements by 1 by default
$post->decrement('views', 10); // Decrements by 10

You can also attach extra updates when decrementing:

php
$post->decrement('views', 1, [
    'updated_at' => date('Y-m-d H:i:s'),
    'modified_by' => Auth::id()
]);

Doppar ORM empowers you to run comprehensive statistical queries, manage data updates, and build analytics-driven features with minimal effort. Whether you're building dashboards, reports, or tracking metrics, these aggregation tools are essential for maintaining efficient and elegant code.

Transform Eloquent Collection

The Doppar ORM offers expressive methods like map(), filter(), and each() to transform, filter, and iterate over Eloquent collections with ease. These methods provide fine control over the shape and content of your data after fetching it from the database.

map() – Transforming Collection Items

Use the map() method to transform each item in a collection. This is especially useful when you want to reformat or limit the fields returned in your response.

Example: Return Only the name of Each User

php
return User::all()
    ->map(function ($item) {
        return [
            'name' => $item->name
        ];
    });

Use case: Customize API responses or prepare data for front-end consumption by trimming down unnecessary fields.

filter() – Conditional Filtering

After transforming a collection, you can use filter() to return only items that match a given condition.

Example: Return Posts Where status = 1, With Only title and status

php
return Post::all()
    ->map(function ($item) {
        return [
            'title' => $item->title,
            'status' => $item->status
        ];
    })
    ->filter(function ($item) {
        return $item['status'] === 1;
    });

each() – Iterating Through Items Without Changing Structure

The each() method is used to perform actions on each item in the collection without modifying the collection itself. It's great for side effects like logging, notifications, or conditional updates.

Example: Run Logic on Each Latest Post

php
return Post::query()->newest()->get()
    ->each(function ($post) {
        // Perform some operation on each post
    });

Summary of Collection Utilities

MethodPurposeMutates Collection?
map()Transforms each item✅ Yes
filter()Filters items by a condition✅ Yes
each()Iterates for side-effects (no return)❌ No

These methods make Doppar’s Eloquent collection manipulation concise, readable, and highly adaptable for API responses, data formatting, and conditional logic.

Pagination

Pagination is an essential feature for working with large datasets, enabling you to fetch and display data in manageable chunks. This improves both performance and user experience, especially in applications that deal with lists like users, posts, products, or logs.

Doppar makes pagination seamless through the built-in paginate() method, which handles all the behind-the-scenes logic for splitting your results into pages.

Basic Usage

To paginate a dataset, simply call the paginate() method like as follows:

php
User::query()->paginate(1);

This will give you the response like this

json
{
    "data": [
        {
            "id": 4,
            "name": "Caden Jerde",
            "email": "stokes.ludwig@hotmail.com",
            "created_at": "2025-05-06 09:59:57",
            "updated_at": "2025-05-06 09:59:57"
        }
    ],
    "first_page_url": "http://example.com/users?page=1",
    "last_page_url": "http://example.com/users?page=21",
    "next_page_url": "http://example.com/users?page=2",
    "previous_page_url": null,
    "path": "http://example.com/users",
    "from": 1,
    "to": 1,
    "total": 21,
    "per_page": 1,
    "current_page": 1,
    "last_page": 21
}

Display Pagination in Views

When working with paginated data in your views, Doppar provides two convenient methods to render pagination links. These methods allow you to display navigation controls for moving between pages, ensuring a smooth user experience.

Available Methods:

  • linkWithJumps() method generates pagination links with additional "jump" options, such as dropdown with paging. It is ideal for datasets with a large number of pages, as it allows users to quickly navigate to the beginning or end of the paginated results.
  • links() This method generates standard pagination links, including "Previous" and "Next" buttons, along with page numbers. It is suitable for most use cases and provides a clean and simple navigation interface.

Now call the pagination for views

html
@foreach ($data['data'] as $user)
    <tr>
        <td>{{ $user->id }}</td>
        <td>{{ $user->name }}</td>
        <td>{{ $user->username }}</td>
        <td>{{ $user->email }}</td>
    </tr>
@endforeach

<!-- "Previous" and "Next" buttons, along with page numbers. -->
{!! paginator($data)->links() !!}

<!-- "Previous" and "Next" buttons, along with page jump options. -->
{!! paginator($data)->linkWithJumps() !!} //

Customize Default Pagination

Doppar provides a Bootstrap 5 pagination view by default. However, you can also customize this view to suit your needs. To customize the pagination view, Doppar offers the publish:pagination pool command. Running this command will create two files, jump.blade.php and number.blade.php, inside the resources/views/vendor/pagination folder. These files allow you to tailor the pagination design to match your application's style.

bash
php pool publish:pagination

Once you modify the jump.blade.php and number.blade.php files, the changes will immediately reflect in your pagination view. This allows you to fully customize the appearance and behavior of the pagination links to align with your application's design and requirements. Feel free to update these files as needed to create a seamless and visually consistent user experience.

Database Transactions

A database transaction is a sequence of database operations that are executed as a single unit. Transactions ensure data integrity by following the ACID properties (Atomicity, Consistency, Isolation, Durability). If any operation within the transaction fails, the entire transaction is rolled back, preventing partial updates that could leave the database in an inconsistent state.

Doppar provides built-in support for handling database transactions using the DB::transaction() method, DB::beginTransaction(), DB::commit(), and DB::rollBack().

Using DB::transaction() for Simplicity

The DB::transaction() method automatically handles committing the transaction if no exception occurs and rolls it back if an exception is thrown.

php
DB::transaction(function () {
    $user = User::create($request->all());
    $post = Post::create([
        'user_id' => $user->id, 
        'title' => 'War is started'
    ]);
});

Manually Handling Transactions

In cases where more control is needed, transactions can be manually started using DB::beginTransaction(). The operations must then be explicitly committed or rolled back.

php
DB::beginTransaction();
try {
    $user = User::create([
        'name' => 'Mahedi',
        'email' => fake()->email,
        'password' => bcrypt('password'),
    ]);

    Post::create([
        'title' => 'My very first post',
        'user_id' => $user->id
    ]);

    DB::commit();
} catch (\Exception $e) {
    DB::rollBack();
    throw $e;
}

Handling Deadlocks with Transaction Retries

Deadlocks can occur when multiple transactions compete for the same database resources. Doppar allows setting a retry limit for transactions using a second parameter in DB::transaction().

php
DB::transaction(function () {
    // Operations that might deadlock
}, 3); // Will attempt up to 3 times before throwing an exception

This approach helps mitigate issues caused by deadlocks by retrying the transaction a set number of times before ultimately failing.

Using transactions properly ensures database consistency and prevents data corruption due to incomplete operations. Doppar provides flexible methods for handling transactions, allowing both automatic and manual control based on the use case.

Eloquent Join

In Doppar, Eloquent manual joins allow you to retrieve data from multiple tables based on a related column. The join method in Eloquent's Query Builder provides an easy way to combine records from different tables. This document explains how to perform various types of joins manually using Eloquent's Eloquent ORM and Query Builder.

Basic Join Example

A simple join operation can be performed using the join method to combine records from two tables based on a common key. Below is an example of joining users and posts tables:

php
$users = User::query()
    ->select('posts.*', 'users.name as user_name')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->get();

This will return a dataset containing user data which has at least one post.

Specifying Join Type

By default, Doppar performs an INNER JOIN. You can specify the type of join you want by passing the join type as an argument:

php
$users = User::query()
    ->join('posts', 'users.id', '=', 'posts.user_id', 'left')
    ->get();

Here, a LEFT JOIN is used to include all users, even if they do not have associated posts.

Applying Conditions in Joins

You can apply additional conditions to filter the joined data. The example below joins the posts table and fetches only the users who have published posts:

php
$users = User::query()
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->where('posts.published', '=', true)
    ->orderBy('users.name', 'ASC')
    ->get();

Performing Multiple Joins

You can join multiple tables in a single query. The example below joins the users, posts, and comments tables:

php
$users = User::query()
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->join('comments', 'posts.id', '=', 'comments.post_id')
    ->get();

This will return data containing users, their posts, and associated comments.

Selecting Specific Columns

To optimize queries and improve performance, you can select specific columns instead of retrieving all fields:

php
$users = User::query()
    ->select('users.name', 'posts.title')
    ->join('posts', 'users.id', '=', 'posts.user_id')
    ->get();

This query retrieves only the users.name and posts.title fields, reducing the amount of data transferred.

DB Query

Doppar’s DB Facade, available via the Phaseolies\Support\Facades\DB namespace, provides a clean, expressive, and powerful interface to interact directly with your database—going beyond the ORM layer when you need low-level control or raw SQL flexibility.

Whether you're running raw queries, managing transactions, executing stored procedures, or querying database views, Doppar gives you a full toolkit under the DB facade.

Basic Database Utilities

OperationExampleDescription
Get all tablesDB::getTables()Returns an array of all table names in the database
Check table existenceDB::tableExists('user')Returns true if the specified table exists
Get table columnsDB::getTableColumns('user')Returns a list of column names from a table
Get table from modelDB::getTable(new User())Fetches the associated table name of a model
Get raw PDO connectionDB::getConnection()Returns the underlying PDO object

Querying the Database with query()

The query() method provided by Doppar enables you to execute raw SQL statements with parameter binding to ensure safe and secure queries. This method returns a \Phaseolies\Support\Collection giving you full control over result handling.

The following example demonstrates how to execute a parameterized SQL query and retrieve the first matching row as an associative array:

php
<?php

use Phaseolies\Support\Facades\DB

$user = DB::query("SELECT * FROM users WHERE name = ?", ['Caden Jerde']);
$user->name;

This will return the first row where the name column matches "Kasper Snider", or null if no such row exists.

Get All Rows

In this example, all rows from the users table are retrieved and stored in the $users collection. If there are no matching rows, an empty array will be returned.

php
$users = DB::query("SELECT * FROM users");

The query() method returns a Collection, allowing you to fluently chain collection methods like map(), filter(), and others. This makes it easy to transform or manipulate the result set directly after querying the database.

If you expect multiple rows, you can use the map() method to transform each row into a custom structure or format.

php
<?php

use Phaseolies\Support\Facades\DB;

return DB::query("SELECT * FROM users")
    ->map(function ($data) {
        return [
            'name' => $data['name'] ?? 'default',
        ];
    });

Modifying the Database using execute()

Doppar provides execute() function and using it, you can modify database. See the basic example. Inserts a new user record. Returns the number of affected rows (1 if successful).

php
$inserted = DB::execute(
    "INSERT INTO users (name, email, password) VALUES (?, ?, ?)",
    ['John Doe', 'john@example.com', bcrypt('secret')]
);

You can use transaction here as well

php
DB::transaction(function () {
    $moved = DB::execute(
        "INSERT INTO archived_posts SELECT * FROM posts WHERE created_at < ?",
        [date('Y-m-d', strtotime('-1 year'))]
    );

    $deleted = DB::execute(
        "DELETE FROM posts WHERE created_at < ?",
        [date('Y-m-d', strtotime('-1 year'))]
    );

    echo "Archived {$moved} posts and deleted {$deleted} originals";
});

Executes multiple statements in a single transaction. Ensures either all queries succeed or none are committed.

Executing Stored Procedures

Doppar provides support for executing stored procedures through the procedure() method. This allows you to call database-stored routines directly, making it ideal for complex logic encapsulated within the database.

By default, procedure() returns a \Phaseolies\Support\Collection results.

php
$nestedResults = DB::procedure('sp_GetAllUsers')->all();

This will return the all users collection data.

Get first row

When you only need the first row from the first result set of a stored procedure, you can use the first() method after calling procedure(). This is useful for cases where the procedure returns a list, but you only care about the top result.

php
$firstUser = DB::procedure('sp_GetAllUsers')->first();

// with passing parameter
$firstUser = DB::executeProcedure('sp_GetAllUsers', [1])->first();

Get last row

When you only need the last row from the first result set of a stored procedure, you can use the last() method after calling procedure(). This is useful for cases where the procedure returns a list, but you only care about the last result.

php
$firstUser = DB::procedure('sp_GetAllUsers')->last();

Get the second result set (index 1) by passing 123 parameter

php
$stats = DB::procedure('sp_GetUserWithStats', [123])->resultSet(1);

With Multiple Params

Doppar's procedure() method supports stored procedures that return multiple result sets. The returned result object offers convenient methods to navigate and extract specific parts of the output.

php
$result = Database::procedure('get_multiple_results');
$result->resultSet(0); // First set
$result->resultSet(1); // Second set

// Get last row of first result set
$lastRow = $result->last();

// Get last row of second result set
$lastRowSet2 = $result->lastSet(1);

Executing View

Doppar makes it easy to query database views using the view() method. A view behaves like a virtual table and can be queried just like a regular table, often used to simplify complex joins or aggregations.

php
$stats = DB::view('vw_user_statistics');

View with WHERE Conditions

You can pass where condition in your custom view like

php
// View with single WHERE condition
$nyUsers = DB::view(
    'vw_user_locations', 
    ['state' => 'New York']
);

// Equivalent to: SELECT * FROM vw_user_locations WHERE state = 'New York'

// View with multiple WHERE conditions
$premiumNyUsers = DB::view(
    'vw_user_locations',
    [
        'state' => 'New York',
        'account_type' => 'premium'
    ]
);

// Equivalent to: 
// SELECT * FROM vw_user_locations 
// WHERE state = 'New York' AND account_type = 'premium'

View with Parameter Binding

You can also pass params with where condition as follows

php
// 4. Using parameter binding for security
$recentOrders = DB::view(
    'vw_recent_orders',
    ['status' => 'completed'],
    [':min_amount' => 100] // Additional parameters
);

// Equivalent to:
// SELECT * FROM vw_recent_orders 
// WHERE status = 'completed' AND amount > :min_amount

Summery

FeatureMethodPurpose
Raw Queriesquery()Run SELECT statements with parameter binding
Data Modificationexecute()Run INSERT, UPDATE, DELETE
Transactionstransaction()Execute multiple queries safely
Stored Proceduresprocedure()Call stored procs with multiple results
Viewsview()Query DB views with filters and bindings
UtilitiesgetTables(), tableExists()Explore schema

Doppar's DB Facade provides all the raw SQL power you need while preserving the developer-friendly syntax you love.