https://github.com/doppar/doppar
Doppar is a PHP framework built to revolutionize the way developers create robust, scalable, and efficient web applications, specifically for developing small, feature-based PHP web applications
https://github.com/doppar/doppar
framework php
Last synced: 3 months ago
JSON representation
Doppar is a PHP framework built to revolutionize the way developers create robust, scalable, and efficient web applications, specifically for developing small, feature-based PHP web applications
- Host: GitHub
- URL: https://github.com/doppar/doppar
- Owner: doppar
- License: mit
- Created: 2025-04-23T15:25:25.000Z (about 1 year ago)
- Default Branch: master
- Last Pushed: 2025-04-25T12:51:02.000Z (about 1 year ago)
- Last Synced: 2025-04-27T09:58:53.756Z (about 1 year ago)
- Topics: framework, php
- Language: PHP
- Homepage: https://doppar.com
- Size: 333 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
## About Doppar
Doppar is a PHP framework built to revolutionize the way developers create robust, scalable, and efficient web applications, specifically for developing small, feature-based PHP web applications.
- **Getting started**
- [Installation](#section-1)
- [Configuration](#section-2)
- [Directory structure](#section-3)
- [Frontend](#section-4)
- [Starter kits](#section-5)
- **Architecture Concepts**
- [Request Lifecycle](#section-7)
- [Service Container](#section-8)
- [Service Providers](#section-9)
- [Facades](#section-41)
- **The Basics**
- [Routing](#section-10)
- [Route Parameter](#section-11)
- [Optional Route Parameter](#section-12)
- [Naming Route](#section-13)
- **Eloquent ORM**
- [Model](#section-44)
- [Model Properties](#section-45)
- [Eloquent ORM Query](#section-46)
- [Eloquent Relationships](#section-51)
- [Transform Eloquent Collection](#section-52)
- [Pagination](#section-47)
- [Database Transactions](#section-53)
- [Manual Join](#section-54)
- **Query Builder**
- [Database Operations with DB Facade](#section-56)
- **Digging Deeper**
- [Middleware](#section-14)
- [Route Middleware](#section-15)
- [Global Web Middleware](#section-16)
- [Middleware Params](#section-17)
- [CSRF Protectection](#section-18)
- [Controllers](#section-19)
- [Request](#section-20)
- [Response](#section-21)
- [Views](#section-22)
- [Asset Bundling](#section-23)
- [Session](#section-24)
- [Cookie](#section-50)
- [Validation](#section-25)
- [Error Handling](#section-26)
- [Error Log and Logging](#section-27)
- [URL Generation](#section-48)
- [Pool Console](#section-28)
- [Encryption & Decryption](#section-30)
- **Database**
- [Database Connection](#section-32)
- [Migration](#section-33)
- [Seeder](#section-34)
- **Authentication**
- [Hashing](#section-35)
- [Authentication](#section-36)
- **Mail**
- [Configuration](#section-37)
- [Sending Mail](#section-38)
- [Sending Mail with Attachment](#section-39)
- [Mail Sending with CC and BCC](#section-40)
- **File Uploads**
- [File Storage](#section-42)
- [Uploads](#section-43)
- **Helpers**
- [Helpers](#section-49)
- **Localization**
- [Localization](#section-55)
- **Package Development**
- [Package Development](#section-57)
- **API Authentication**
- [Doppar Flarion](#flarion)
## Installation
Before creating your first Doppar application, make sure that your local machine has PHP, Composer. If you don't have `PHP` and `Composer` installed on your local machine, the following commands will install PHP, Composer on macOS, Windows, or Linux:
#### macOS
```bach
/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.4)"
```
#### Windows PowerShell
```bach
# Run as administrator...
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows/8.4'))
```
#### Linux
```bash
/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.4)"
```
After you have installed PHP, Composer, you're ready to create a new Doppar application.
#### Create an Application
```bash
composer create-project doppar/doppar example-app
```
Now run a command to generate application key that will use to encrypt and decrypt sensitive data.
```bash
php pool key:generate
```
This command will set `APP_KEY` in your `.env` file.
Now you are ready to start development server, run this command:
```bash
php pool start
```
Use `--post` as a params in command to run application in specific post like
```bash
php pool start --port=8081
```
Now your development server is ready.
## Configuration
All of the configuration files for the Doppar framework are located in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you.
> **⚠️ Warning:** If you create a new config file, Doppar do not know about it, to let it know to Doppar, need to run
>
> ```bash
> php pool config:clear
>
> ```
>
> Proceed on now
If you run system, Doppar cached your newly created config file again. You can cache config file manyally by running this command
```php pool config:cache```
Doppar's config cache file located `storage/framework/cache/config.php` path
### Accessing Configuration Values
You may easily access your configuration values using the Config facade or global config function from anywhere in your application. The configuration values may be accessed using "`dot`" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist:
```php
## Directory structure
```
└── 📁doppar-framework
└── 📁app
└── 📁Http
└── 📁Controllers // Controllers directory
└── Kernel.php // Register Application global and route specific middleware
└── 📁Middleware // Middleware located here
└── 📁Models // Application model directory
└── 📁Mail // Application mail directory
└── 📁Providers // Application Service Provider
└── 📁bootstrap // Bootstrap application dependency from here
└── 📁config // Config files located here
└── 📁database
└── 📁migrations // Database migration files
└── 📁seeds // Database seeder files
└── 📁public // Public directory for assets
└── 📁resources
└── 📁views // All the views will be located here
└── 📁routes // Application routes directory
└── 📁storage
└── 📁app
└── 📁public // Public symlinked to /public/storage directory to storage/app/public directory
└── 📁cache // All the views cache and config files cache located here
└── 📁logs // System error log and user defined log will be printed here
└── 📁sessions // file session driver will be cache here
└── .env
└── .env.example
└── .gitignore
└── composer.json
└── composer.lock
└── config.example
└── config.php // Database seeder and migration command setup file
└── pool // Pool command
└── README.md
```
## Frontend
Doppar uses its own template engine blade. See some avaiable blade syntax.
#### Conditionals and Loops
### @if
Checks if a condition is true.
```blade
@if ($condition)
// Code to execute if the condition is true
@endif
```
### @elseif
Checks an additional condition if the previous `@if` or `@elseif` condition is false.
#### Syntax:
```blade
@if ($condition1)
// Code for condition1
@elseif ($condition2)
// Code for condition2
@endif
```
### @else
Executes code if all previous `@if` and `@elseif` conditions are false.
#### Syntax:
```blade
@if ($condition)
// Code for condition
@else
// Code if condition is false
@endif
```
### @unless
Executes code if the condition is false.
#### Syntax:
@unless ($condition)
// Code to execute if the condition is false
@endunless
### @isset
Checks if a variable is set and not null.
#### Syntax:
```blade
@isset ($variable)
// Code to execute if the variable is set
@endisset
```
### @unset
Unsets a variable.
#### Syntax:
```blade
@unset ($variable)
```
### @for
Executes a loop for a specified number of iterations.
#### Syntax:
```blade
@for ($i = 0; $i < 10; $i++)
// Code to execute in each iteration
@endfor
```
### @foreach
Iterates over an array or collection.
#### Syntax:
```blade
@foreach ($items as $item)
// Code to execute for each item
@endforeach
```
### @forelse
Iterates over an array or collection, with a fallback if the array is empty.
#### Syntax:
```blade
@forelse ($items as $item)
// Code to execute for each item
@empty
// Code to execute if the array is empty
@endforelse
```
### @while
Executes a loop while a condition is true.
#### Syntax:
```blade
@while ($condition)
// Code to execute while the condition is true
@endwhile
```
### @switch
Executes a switch-case block.
#### Syntax:
```blade
@switch ($variable)
@case ($value1)
// Code for value1
@break
@case ($value2)
// Code for value2
@break
@default
// Default code
@endswitch
```
### @break
Breaks out of a loop or switch-case block.
#### Syntax:
```blade
@break
```
Example
```blade
@foreach ($users as $user)
@if ($user->isBanned)
@break
@endif
{{ $user->name }}
@endforeach
```
### @continue
Skips the current iteration of a loop.
#### Syntax:
```blade
@continue
```
Example:
```blade
@foreach ($users as $user)
@if ($user->isBanned)
@continue
@endif
{{ $user->name }}
@endforeach
```
### @php
Executes raw PHP code.
#### Syntax:
```blade
@php
// PHP code
@endphp
```
### @json
Encodes data as JSON.
#### Syntax:
```blade
@json ($data)
```
Example
```blade
var users = @json($users);
```
### @csrf
Generates a CSRF token for forms.
#### Syntax:
```blade
@csrf
```
Example
```blade
@csrf
Submit
```
### @exit
Usage
```blade
@foreach ($users as $user)
@if ($user->isAdmin())
@exit
@endif
@endforeach
```
### @empty
Usage
```blade
@empty ($users)
// User is empty
@endempty
```
## Section Directives
### @extends
```blade
@extends('layouts.app')
```
Extends a Blade layout.
### @include
```blade
@include('partials.header')
```
Includes another Blade view
### @yield
```blade
@yield('content')
```
Outputs the content of a section.
### @section
Example:
```blade
@section('content')
This is the content section
@endsection
```
Defines a section to be yielded later
### @stop
```blade
@stop
```
Stops the current section output.
### @overwrite
```blade
@overwrite
```
Overwrites the current section content.
### Authentication Directives
### @auth
Checks if a user is authenticated.
#### Syntax:
```blade
@auth
// Code to execute if the user is authenticated
@endauth
```
### @guest
Checks if a user is not authenticated (guest).
#### Syntax:
```blade
@guest
// Code to execute if the user is not authenticated
@endguest
```
### Flash Message Directives
### @errors
Checks if there are any errors messages.
#### Syntax:
```blade
@errors
// Code to execute if errors messages exist
@enderrors
```
Example
```blade
@errors
@foreach (session()->get('errors') as $field => $messages)
@foreach ($messages as $message)
- {{ $message }}
@endforeach
@endforeach
@enderrors
```
### @error
Checks if a specific error message exists in the flash messages.
#### Syntax:
```blade
@error('key')
// Code to execute if the error message exists
@enderror
```
Example
```blade
@error('email')
{{ $message }}
@enderror
```
## Starter kits
To give you a head start building your new Doppar application, we are happy to offer application starter kits. These starter kits give you a head start on building your next Doppar application, and include the routes, controllers, and views you need to register and authenticate your application's users.
```html
layout/app.blade.php
@yield('title')
@section('style')
// This for loading ondemand page css
@show
@yield('content')
@section('script')
// This for loading ondemand page script
@show
```
Extented content file
```html
@extends('layouts.app')
@section('title') Home Page
@section('style')
// Load css
@append
@section('content')
Dashboard
Howdy
@endsection
@section('script')
// Load script
@append
```
## Request Lifecycle
Understanding how a tool operates makes using it much easier and more intuitive. This principle applies just as much to application development as it does to any other tool in the real world. When you comprehend the inner workings of your development tools, you gain confidence and efficiency in using them.
This document aims to provide a high-level overview of how the Doppar framework functions. By familiarizing yourself with its core concepts, you’ll reduce the sense of mystery surrounding it and feel more empowered when building applications. Don’t worry if some of the terminology seems unfamiliar at first—focus on grasping the big picture. As you continue exploring the documentation, your understanding will naturally deepen over time.
### Lifecycle Overview
#### First Steps
The entry point for all requests to a Doppar application is the `public/index.php` file. All requests are directed to this file by your web server (Apache / Nginx) configuration. The index.php file doesn't contain much code. Rather, it is a starting point for loading the rest of the framework.
The `index.php` file loads the Composer generated autoloader definition, and then retrieves an instance of the Doppar application from `bootstrap/app.php`. The first action taken by Doppar itself is to create an instance of the application service container.
### Second Steps
At this stage, Doppar initializes its core configuration and loads the service providers along with their registered methods. Once the core setup is complete, Doppar proceeds to load and execute the boot methods of the registered service providers, ensuring that all necessary services are properly initialized and ready for use.
### Thirds Steps
At this stage, Doppar generate global middleware stack. The method signature for the middleware's `__invoke()` method is quite simple: it receives a Request and Closer $next and send the Request and Response to the next Request. Feed it HTTP requests and it will return HTTP responses.
### Final Steps
Once the application has been fully bootstrapped and all service providers have been registered, the request is passed to the router for processing. The router is responsible for directing the request to the appropriate route or controller while also executing any route-specific middleware.
Middleware acts as a powerful filtering mechanism for incoming HTTP requests, allowing the application to inspect, modify, or restrict access before reaching the intended destination. For instance, Doppar includes authentication middleware that verifies whether a user is logged in. If the user is not authenticated, they are redirected to the login screen; otherwise, the request proceeds as expected.
After the designated route or controller method processes the request and generates a response, the response begins its journey back through the middleware stack. This allows the application to inspect or modify the outgoing response before it reaches the user.
Finally, as the response completes its trip through the middleware, the __invoke method returns the response object, which then calls the send method. The send method delivers the response content to the user's web browser, marking the completion of Doppar’s request lifecycle.
## Service Container
The Doppar Service Container is a robust and versatile tool designed to streamline dependency management and facilitate dependency injection within application. At its core, dependency injection is a sophisticated concept that simplifies how class dependencies are handled: instead of a class creating or managing its own dependencies, they are "injected" into the class—typically through the constructor or, in certain scenarios, via setter methods. This approach promotes cleaner, more modular, and testable code, making the Doppar framework an ideal choice for modern, scalable web development.
Let's look at a simple example:
```php
getUser();
$users = $this->user->all();
return view('welcome', compact('userServiceData','users'));
}
}
```
In this example, the UserInterface from controller `index` method and User class from constructor will automatically injected in Doppar's request life cycle and will be automatically instantiated.
### Create Instance Using Container
Doppar allows you to create object of a class using global `app()` function. If you want to create object that works like a singleton object, you can use the `app()` function
```php
$singletonObject = app(SMSService:class); // this is the object of SMSService class
$singletonObject->sendSms();
```
But if you call just `app()`, it will return the Application instance.
## Binding
### bind()
Binds a service to the container. You can bind a class name or a callable (closure) that returns an instance of the service.
```php
bind(string $abstract, callable|string $concrete, bool $singleton = false): void
```
Parameters:
1. `$abstract:` The service name or interface.
2. `$concrete:` The class name or a callable that returns an instance.
3. `$singleton:` Whether the binding should be a singleton (optional, default: false)
Example
```php
app->bind(ProductInterface::class, fn() => new ProductClass );
$this->app->bind(ProductInterface::class, ProductClass::class);
}
public function boot(): void
{
//
}
}
```
## Singleton Bindings
### singleton()
Binds a service as a singleton. The container will always return the same instance when resolving the service.
Example
```php
$this->app->singleton(ProductInterface::class,fn() => new ProductClass);
```
## Conditional Bindings
#### when()
Conditionally binds a service based on a condition. If the condition is true, the binding is applied.
#### Syntax:
```php
when(callable|bool $condition): ?self
```
Example
```php
$this->app->when(fn() => rand(0, 1) === 1)?->singleton(ProductInterface::class,fn() => new ProductClass);
```
The Doppar Service Container simplifies dependency management by providing a clean and intuitive API for binding and resolving services. Whether you're working with regular bindings, singletons, or conditional logic, the container ensures your application remains modular, testable, and scalable.
## Service Providers
Service providers are the central place of all Doppar application bootstrapping. Your own application, as well as all of Doppar's core services, are bootstrapped via service providers.
But, what do we mean by "bootstrapped"? In general, we mean registering things, including registering service container bindings, configuration initialization, middleware, and even routes and all the facades. Service providers are the central place to configure your application.
Doppar uses service providers internally to bootstrap its core services, such as the mailer, cache, facades and others.
All user-defined service providers are registered in the `config/app.php` file. In the following documentation, you will learn how to write your own service providers and register them with your Doppar application.
### Creating Service Providers
Doppar provides `make:provider` pool command to create a new service provider.
```bash
php pool make:provider MyOwnServiceProvider
```
### The Register Method
As mentioned previously, within the register method, you should only bind things into the service container. You should never attempt to register other services. Otherwise, you may accidentally use a service that is provided by a service provider which has not loaded yet.
Let's take a look at a basic service provider. Within any of your service provider methods, you always have access to the $app property which provides access to the service container:
```php
app->singleton(Connection::class, function (Application $app) {
return new Connection('test');
});
}
}
```
### The Boot Method
This `boot` method is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework:
```php
## Facades
Throughout the Doppar documentation, you will see examples of code that interacts with Doppar's features via "facades". Facades provide a "static" interface to classes that are available in the application's service container. Doppar ships with many facades which provide access to almost all of Doppar's features.
Doppar facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. It's perfectly fine if you don't totally understand how facades work - just go with the flow and continue learning about Doppar.
All of Doppar's facades are defined in the `Phaseolies\Support\Facades` namespace. So, we can easily access a facade like so:
```php
use Phaseolies\Support\Facades\Config;
use Phaseolies\Support\Facades\Route;
Route::get('/config', function () {
return Config::get('key');
});
```
Throughout the Doppar documentation, many of the examples will use facades to demonstrate various features of the framework.
To complement facades, Doppar offers a variety of global "helper functions" that make it even easier to interact with common Doppar features. Some of the common helper functions you may interact with are view, response, url, config, and more. Each helper function offered by Doppar.
For example, instead of using the `Phaseolies\Support\Facades\Response` facade to generate a JSON response, we may simply use the response function. Because helper functions are globally available, you do not need to import any classes in order to use them:
```php
use Phaseolies\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
```
### When to Utilize Facades
Facades have many benefits. They provide a terse, memorable syntax that allows you to use Doppar's features without remembering long class names that must be injected or configured manually. Furthermore, because of their unique usage of PHP's dynamic methods, they are easy to test.
### Facades vs. Helper Functions
In addition to facades, Doppar includes a variety of "helper" functions which can perform common tasks like generating views, firing events, dispatching jobs, or sending HTTP responses. Many of these helper functions perform the same function as a corresponding facade. For example, this facade call and helper call are equivalent:
```php
return Phaseolies\Support\Facades\Response::view('profile');
return view('profile');
```
### How Facades Work
In a Doppar application, a facade is a class that provides access to an object from the container. The machinery that makes this work is in the Facade class. Doppar's facades, and any custom facades you create, will extend the base `Phaseolies\Support\Facades\BaseFacade` class.
The Facade base class makes use of the __callStatic() magic-method to defer calls from your facade to an object resolved from the container. In the example below, a call is made to the Doppar cache system. By glancing at this code, one might assume that the static get method is being called on the Cache class:
```php
## Routing
Doppar provides a comprehensive and flexible routing system that allows you to define how your application responds to various HTTP requests. All routes are initialized from the routes/web.php file, making it easy to manage and organize your application's endpoints.
### Supported HTTP Methods
Doppar supports the following HTTP methods for defining routes:
* `GET`: Used to retrieve data from the server.
* `POST`: Used to submit data to the server.
* `PUT`: Used to update existing data on the server.
* `PATCH`: Used to partially update existing data on the server.
* `DELETE`: Used to delete data on the server.
* `HEAD`: Similar to GET, but retrieves only the headers without the body.
* `OPTIONS`: Used to describe the communication options for the target resource.
* `ANY`: Matches any HTTP method for the specified route.
```php
## Route Parameter
You can pass single or multiple parameter with route as like below
```php
## Optional Route Parameter
Doppar accept optional parameter and for this, you have nothing to do.
> **⚠️ Warning:** By default, the Doppar controller callback takes parameters as optional. So, if you pass parameters in your route but do not accept them in the controller, it will not throw an error.
>
Example
```php
Route::get('/user/{id}/{username}', [ProfileController::class, 'index']);
```
if you visit `http://localhost:8000/user/1/mahedi-hasan`
```php
## Naming Route
Doppar support convenient naming route structure. To create a naming route, you can do
```php
use Phaseolies\Support\Facades\Route;
use App\Http\Controllers\UserController;
Route::get('/user/{id}/{name}', [UserController::class, 'profile'])->name('profile');
```
Now use this naming route any where using route() global method.
```php
@csrf
Submit
```
If there is single param in your route, just use
```php
Route::get('/user/{id}', [UserController::class, 'profile'])->name('profile');
```
Now call the route
```php
{{ route('profile', 2) }}
```
### Define PUT, PATCH, DELETE Request Route
If you want to define route as a PUT, PATCH or DELETE, need to follow some rules. Assume, we are going to send a PUT request to the server, then you have to use @method('PUT') blade directive inside your HTML form.
```html
@csrf
@method('PUT') // For PUT Request
@method('PATCH') // For PATCH Request
@method('DELETE') // For DELETE Request
Submit
```
Now you are ready to define `PUT` route in `web.php` file
```php
Route::put('/update-profile', [ProfileController::class, 'updateProfile']);
// For DELETE and PATCH
Route::delete('/user/{id}', [ProfileController::class, 'delete']);
Route::patch('/update-profile', [ProfileController::class, 'updateProfile']);
```
### Route Group
`Route::group` is used to group multiple routes under a shared configuration like middleware, URL prefix, namespace, etc. This helps in organizing routes cleanly and applying common logic to them.
#### Syntax
```php
Route::group([
'prefix' => 'your-prefix',
'middleware' => ['middleware-name']
], function () {
// Routes go here
});
```
#### Example
Look at the below example, we are using `prefix` and `middleware` as a group route. You can use either `prefix` or `middleware` or both. See the example
```php
Route::group([
'prefix' => 'login',
'middleware' => ['guest']
], function () {
Route::get('/', [LoginController::class, 'index'])->name('login'); // url will be example.com/login
Route::post('/', [LoginController::class, 'login']); // url will be example.com/login
});
```
`'prefix' => 'login'` This means all routes inside this group will be prefixed with `/login` and `'middleware' => ['guest']` Applies the guest` middleware to both routes.
### Route Caching
Doppar supports route caching for improved performance in production environments. Route caching is controlled by the `APP_ROUTE_CACHE` environment variable: Set to `true` to enable route caching (recommended for production). Set to `false` to disable (default for development) Route cache files are stored in: `storage/framework/cache/`
#### Cache Routes
Generate a route cache file for faster route registration:
```bash
php pool route:cache // Note: This should be run after any route modifications.
```
#### Clear Route Cache
```bash
php pool route:clear // Use this when making route changes in production or if experiencing route-related issues.
```
## Eloquent ORM
### 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:
```php
php pool make:model Invoice
```
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
## 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`, which allows you to hide sensitive or irrelevant data from being exposed. 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.
```php
## 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
use App\Models\Invoice;
foreach (Invoice::all() as $invoice) {
echo $invoice->invoice_id;
}
// You can also use Invoice::get() to get all the records
```
### Building Queries
The Eloquent all method will return all of the results in the model's table. However, since each Eloquent model serves as a query builder, you may add additional constraints to queries and then invoke the get method to retrieve the results:
```php
$flights = Invoice::query()
->where('active', '=', 1)
->orderBy('name')
->limit(10)
->get();
```
### Multiple Where Condition
You can write query with multiple where condition by chaining multiple where with the queries
```php
User::query()
->where('name', '=', 'Sincere Littel')
->where('username', '=', 'doppar')
->first();
```
You can also add `orWhere()` as like below
```php
User::query()
->where('name', '=', 'Sincere Littel')
->orWhere('username', '=', 'test')
->get();
```
### Fetch the First Records
To fetch the first records you can use `first` function as like below
```php
User::query()->first();
```
### 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()
You can get the sql query to see which query is running to fetch the records like
```php
User::query()->orderBy('id', 'desc')->groupBy('name')->toSql();
```
### Count The Result
To see how many records your table has, you can use `count` function as like
```php
User::count();
// or
User::query()->orderBy('id', 'desc')->groupBy('name')->count();
// or you can pass the column name which you need to count
User::query()->where('active', '=', true)->count('id');
```
### Newest and Oldest Records
To fetch the oldest and newest records, Doppar provides two function `newest` and `oldest`.
```php
User::query()->newest()->get();
// You can explicitly define which field should be used to retrieve the latest records. By default, it uses the 'id' field.
User::query()->newest('name')->get()
User::query()->oldest()->get()
// You can explicitly define which field should be used to retrieve the latest records. By default, it uses the 'id' field.
User::query()->oldest('id')->get()
```
### select()
You may need to fetch only some specific columns not all the columns from a model. you can use select() method in this case like
```php
User::query()->select(['name','email'])->get();
```
### find() and exists()
If you need to retrieve data for a specific primary key, you can use the `find()` function. This method allows you to quickly fetch a single record by its unique identifier, making it a convenient and efficient way to access individual entries in your database.
```php
User::find(1)
```
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
User::query()->where('id', '=', 1)->exists(); // Returns `true` if a matching row exists, otherwise `false`.
```
### Create Data
To create data, Doppar uses the `create` method. It retrieves the attributes defined in your model's `$creatable` property and inserts them into the database. For example:
```php
use App\Models\User;
User::create([
'name' => 'Doppar'
]);
```
Or you also create data using object
```php
$user = new User;
$user->name = $request->name;
$user->Save();
```
### saveMany()
You may need to insert bacth insert in your model, in this case, you can use `saveMany()` method as like below
```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')]
]);
```
If your dataset is large, you can optionally pass the chunk size as the second parameter like
```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')]
], 100);
```
### Update Data
To update data, you can use `fill` function like
```php
$user = User::find(1);
$user->fill(['name' => 'Updated Name']);
$user->save();
```
Or you can use `update` function like
```php
User::query()
->where('id', '=', 1)
->update([
'email' => 'hi@doppar.com'
]);
```
### updateOrCreate()
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' => 'hi@doppar.com'], // attributes to match
['name' => 'Test User'] // values to update/create
);
```
### Delete Data
To delete data, doppar provides `delete` method
```php
User::find(1)->delete();
```
### whereIn()
The `whereIn()` method filters records where a column's value matches any value in the given array.
```php
return 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
return User::query()->orWhereIn('id', [1, 2, 4])->get();
```
### whereBetween()
The `whereBetween()` method allows you to filter records where a given column's value falls within a specified range. This is commonly used for date or numerical ranges.
```php
return User::query()
->whereBetween('created_at', ['2025-02-29', '2025-04-29'])
->get();
```
### 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
return User::query()
->whereNotBetween('created_at', ['2025-02-29', '2025-04-29'])
->get();
```
### orWhereBetween()
You can use `orWhereBetween()` methods together to create complex conditional queries. This allows for more flexible filtering, including combining conditions with OR for specific ranges.
```php
return Post::query()
->where('status', ,'=', 'published') // Filter posts that are published
->orWhereBetween('views', [100, 500]) // Or filter posts where views are between 100 and 500
->get();
```
### orWhereNotBetween()
You can use `orWhereNotBetween()` methods together to create complex conditional queries. This allows for more flexible filtering, including combining conditions with OR for specific ranges.
```php
return Post::query()
->where('status', ,'=', 'published')
->orWhereNotBetween('views', [100, 500])
->get();
```
With request input example
```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();
```
### whereNull()
The `whereNull()` method in Eloquent is used to filter records where a specific column contains a NULL value. In your example:
```php
return Post::query()
->whereNull('created_at')
->get();
```
### whereNotNull()
The `whereNotNull()` method in Eloquent is used to filter records where a specific column contains a value. In your example:
```php
Post::query()
->whereNotNull('published_at')
->get();
```
### 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') // you can also use orWhereNotNull()
->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();
```
### match()
The `match()` method in Eloquent is commonly used to filter your query result. It's typically implemented to provide a more flexible and reusable way to filter results based on various conditions.
```php
return Post::match([
'id' => 1,
'user_id' => 1
])->get();
```
Complex filtering with callbacks
```php
return Post::match(function ($query) {
$query->where('views', '>', 100)
->whereBetween('created_at', ['2023-01-01', '2023-12-31']);
})->get();
```
Request data filter, this will fetch data as per requested `title` and `user_id`
```php
Post::match($request->only(['title', 'user_id']))
->paginate();
```
Combine simple and complex filters
```php
Post::match([
'user_id' => [1, 2, 3], // WHERE IN
'created_at' => null, // WHERE NULL
'active' => function ($query) {
$query->where('active', '=', 1)
->orWhere('legacy', '=', 1);
}
])->orderBy('created_at', 'desc')
->get();
```
### pluck()
The `pluck()` method is used to retrieve the values of a single column from the result set. It returns an array or collection of the specified column's values, making it useful for quickly getting a list of specific data
```php
return Post::query()->pluck('title');
```
### useRaw() for Custom Queries with Parameter Bindings
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
return User::query()->useRaw(
'SELECT * FROM user WHERE created_at > ? AND status = ?',
['2023-01-01', 'active']
);
```
is 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.
### Introduction
Database tables are often interconnected, representing real-world relationships between data. For instance, a blog post can have multiple comments, or an order may be linked to the user who placed it. Eloquent simplifies handling these relationships, providing built-in support for various types. Doppar supports three eloquent relationships.
- One-to-One
- One-to-Many
- Many-to-Many
### Defining Relationships (One-to-One)
Eloquent allows you to define relationships as methods within your model classes, enabling seamless method chaining and advanced query capabilities. This makes it easy to interact with related models while maintaining clean and efficient code.
For instance, let's assume each `Article` has a single associated `User` (author). We can define this one-to-one relationship in the `Article` model as follows:
```php
oneToOne(
User::class, // Related model class
'id', // Foreign key on related table (users.id)
'user_id' // Local key on this table (articles.user_id)
);
}
}
```
## Key Parameters for Defining Relationships
When defining relationships in your Eloquent models, you'll typically use these key parameters:
* **Related Model:**
* This specifies the class name of the model you're establishing a relationship with.
* Example: `User::class`
* **Foreign Key:**
* This refers to the column in the *related* table that stores the foreign key, which references the primary key of the current model.
* It is the column that makes the connection between the 2 tables.
* **Local Key:**
* This is the column in the *current* model's table that is being referenced by the foreign key in the related table.
* Usually this is the primary key of the current table.
Once defined, you can access the relationship in several ways:
#### As a Property (Lazy Loading)
```php
$article = Article::find(1);
$author = $article->user; // Automatically loads the related user
```
Fetching all the articles of user id 1
```php
return User::find(1)
->articles() // fetching all the articles of user id 1
->get();
```
Or you can fetch above data as follows also
```php
return User::find(1)->articles;
```
#### As a Method (Query Builder)
```php
$article = Article::find(1);
$activeAuthor = $article->user()
->where('status', '=', 1)
->first();
```
#### With Eager Loading
```php
// Load articles with their authors in a single query
$articles = Article::query()->embed('user')->get();
foreach ($articles as $article) {
echo $article->user->name; // No additional query executed
}
```
#### embed() with sub query
This will return articles with user data where the user status is 1. If the user status is 0, it will return null for the user but still load the articles.
```php
return Article::query()->embed(['user' => function ($query) {
$query->where('status', '=', 1);
}])->get();
```
### Defining Relationships (One-to-Many)
For instance, let's assume each `User` has its associated `articles`. We can define this one-to-many relationship in the `User` model as follows:
```php
oneToMany(
Article::class, // Related model class
'user_id', // Foreign key on related table (articles.user_id)
'id' // Local key on this table (users.id)
);
}
}
```
Now you can fetch all the users with their associated articles.
```php
return User::query()->embed('articles')->get();
```
### Defining Relationships (Many-to-Many)
Doppar ORM supports many-to-many relationships through an intermediate pivot table. Here's how to implement and work with them. Assume we have a `Post` model and a `Tag` model, where a many-to-many relationship exists between them. This means one post can have multiple tags, and one tag can be associated with multiple posts.
```php
// App\Models\Post.php
public function tags()
{
return $this->manyToMany(
Tag::class, // Related model
'post_id', // Foreign key for posts in pivot table (post_tag.post_id)
'tag_id', // Foreign key for tags in pivot table (post_tag.tag_id)
'post_tag' // Pivot table name
);
}
```
And the Tag model will be look like this
```php
// App\Models\Tag.php
public function tags()
{
public function posts()
{
return $this->manyToMany(
Post::class, // Related model
'tag_id', // Foreign key for tags in pivot table
'post_id', // Foreign key for posts in pivot table
'post_tag' // Pivot table name
);
}
}
```
In a many-to-many relationship between `Post` and `Tag`, the `post_tag` pivot table acts as a bridge, storing the associations between `posts` and `tags`. It contains two columns:
- `post_id` – References the id of a post.
- `tag_id` – References the id of a tag.
Each row in this table represents a link between a specific post and a specific tag, allowing multiple posts to have multiple tags and vice versa.
#### Retrieving Related Models From Many To Many Relationship
We can fetch data from many to many relationship using eager loading like that
```php
return Post::query()->embed('tags')->get();
```
You can fetch Tag with posts as well
```php
return Tag::query()->embed('posts')->get();
```
### Many To Many Relationship with Lazy loading
In Doppar, when dealing with a many-to-many relationship, lazy loading allows you to retrieve related records only when they are accessed. For instance, if a Post model has a many-to-many relationship with a Tag model, you can fetch all the tags of a specific post using the following query:
```php
$post = Post::find(1);
return $post->tags;
// or simply you can call like that
return $post = Post::find(1)->tags;
```
For instance, if a Tag model has a many-to-many relationship with a Post model, you can fetch all the posts of a specific tag using the following query:
```php
$tag = Tag::find(1);
return $tag->posts;
// Get the posts related to a specific tag
foreach ($tag->posts as $post) {
echo $post->title;
}
```
### link() with manyToMany
In Doppar, the `link()` method is used to associate records in a many-to-many relationship. This method adds entries to the pivot table, establishing a connection between related models
```php
// Link tags to a post
$post = Post::find(1);
$post->tags()->link([1, 2, 3]); // Link tags with IDs 1, 2, and 3
```
### unlink() with manyToMany
In Doppar, the `unlink()` method is used to unlink specific records from a many-to-many relationship. It removes the association between the current model (e.g., Post) and the related model (e.g., Tag) by removing the corresponding entries from the pivot table.
```php
$post = Post::find(1);
$post->tags()->unlink([1, 2, 3]); // Unlink tags with IDs 1, 2, and 3
```
If you simply call `unlink()`, it will delete all the tags
```php
$post = Post::find(1);
$post->tags()->unlink(); // unlink all tags
```
### relate() with manyToMany
In Doppar, the `relate()` method is used to sync the relationships between models in a many-to-many relationship. The `relate()` method will attach the provided IDs and can optionally detach existing relationships, depending on the second argument passed.
```php
$post = Post::find(1);
$post->tags()->relate([1, 2, 3]);
$changes = $post->tags()->relate([1, 2, 4]); // 3 will be removed
$post->tags()->relate([1, 2, 3], false); // link tithout unlinking
```
### Syncing with Pivot Data Using
In Doppar, the `relate()` method not only allows you to sync records in a many-to-many relationship, but also provides the ability to attach additional data to the pivot table. This is useful when you need to store extra attributes (such as timestamps or other metadata) along with the relationship between two models.
```php
$post = Post::find(1);
$post->tags()->relate([
1 => ['created_at' => now()],
2 => ['featured' => true],
3 => ['meta' => ['color' => 'blue']]
]);
```
### Nested Relationship
To efficiently retrieve all posts along with their associated comments, replies, and the users who made those replies, use the following query:
```php
return Post::query()->embed('comments.reply.user')->get();
```
#### Breakdown:
- comments → Fetches all comments related to the post.
- reply → Fetches replies associated with each comment.
- user → Fetches the user who authored each reply.
By using eager loading (embed), this query minimizes database queries and optimizes performance, ensuring efficient data retrieval.
### Specific Column Selection
The embed() method in Doppar ORM allows you to eager load related models and even specify which columns to load for each relationship. This helps optimize queries by only selecting the necessary data.
```php
User::query()
->embed([
'comments.reply', // Load all columns of the related comments
'posts' => function ($query) {
$query->select(['id', 'title', 'user_id']); // Only load specific columns for posts
}
])
->get();
```
#### Fetching Multiple Relationships
This query retrieves `users` along with their related `articles` and `address` using the embed method. By embedding multiple relationships, it ensures that all necessary data is fetched in a single query, improving efficiency and reducing additional database calls.
```php
return User::query()
->embed(['articles','address'])
->get();
```
### Using `present()` for Handling Present Relationships
In Doppar ORM, the `present()` method can be used to load relationships that are present (i.e., do not exist) in the model, or when you want to ensure related data is included, even if it is not empty.
```php
return Post::query() // Fetch only those posts which have comments
->present('comments') // Load the 'comments' relationship
->get();
```
This method is useful when you want to include relationships and need to fetch only those data that has relational data.
#### Passing callback with present()
The `present()` method can be used to load a relationship with custom query conditions. You can define specific conditions inside the closure passed to `present()` to filter the related data.
```php
return Post::query()
->present('comments', function ($query) {
$query->where('comment', '=', 'Mrs.') // Filter comments with the text 'Mrs.'
->where('created_at', '=', NULL); // Only fetch comments with no creation date
})
->get();
```
### absent() to Fetch Records with Missing Relationships
The `absent()` method is used to fetch records where a particular relationship does not exist. This is useful when you want to retrieve records that are missing related data.
```php
return User::query() // Fetch only those users who have no posts
->absent('posts') // Filter users with no related posts
->get();
```
### if() for Conditional Query Execution in Doppar ORM
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.
```php
Post::query()
->if($request->input('search'),
fn($q) => $q->where('title', 'LIKE', "%{$request->search}%"),
fn($q) => $q->where('is_featured', '=', true) // default if no search is applied, and it is optional
)
->get();
```
#### Explanation:
- First condition `($request->input('search'))`: If the search parameter is provided, the query will filter posts by the title using the LIKE operator.
- Default case: If no search parameter is provided, it will filter 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.
### present() with if()
You can chain the `present()` method with `if()` to conditionally load a relationship with specific query constraints, based on dynamic conditions. This allows for more flexible and powerful query building.
```php
return Post::query()
->present('comments', function ($query) {
$query->where('comment', '=', 'Mrs.') // Filter comments with 'Mrs.'
->where('created_at', '=', NULL); // Only include comments with no creation date
})
->if($request->title, function ($query) use ($request) {
$query->where('title', '=', $request->title); // Apply title filter if provided in the request
})
->get();
```
### ifExists() check relationship existance
The `ifExists()` method in Doppar is used as a conditional check to determine whether a related model (e.g., posts) exists in the database for a given parent model (e.g., users). This method is useful for filtering results based on the existence of related data without requiring explicit joins or additional queries.
```php
// Find users who have at least one post
return User::query()->ifExists('posts')->get();
```
With conditions - find users who have at least one published post
```php
return User::query()
->ifExists('posts', function ($query) {
$query->where('status', '=', 'published');
})
->get();
```
### ifNotExists() check relationship non-existance
In the Doppar framework, the `ifNotExists()` method works similarly to the `ifExists()` method but with the inverse logic. Instead of filtering users who have at least one post, it retrieves users who don't have any related posts. This can be useful when you want to find records without any associated data.
```php
// Find users who don't have any posts
return User::query()->ifNotExists('posts')->get();
```
### Aggregation Queries using ORM
Doppar ORM provides powerful aggregation functions to perform statistical calculations efficiently. Below are various examples of aggregation queries you can use in your application.
#### Total Sum of a Column
Calculate the total sum of a column (e.g., summing up all views values):
```php
Post::query()->sum('views');
```
#### Average Value of a Column
Compute the average value of a column (e.g., the average views):
```php
return Post::query()->avg('views');
```
#### Maximum Value in a Column
Find the highest price in the Product table:
```php
Product::query()->max('price');
```
#### Minimum Value in a Column
Find the lowest `price` in the Product table:
```php
Product::query()->max('price');
```
#### Standard Deviation Calculation
Compute the standard deviation of `price`:
```php
return Product::query()->stdDev('price');
```
#### Variance Calculation
Find the variance of `price`:
```php
return Product::query()->variance('price');
```
#### Multiple Aggregation in One Query
Retrieve multiple statistics (`count`, `average`, `min`, `max`) in a single 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();
```
#### Fetching Distinct Rows
You can also fetch `distinct` rows from your table by chanining the `distinct()` function as like below. This will fetch unique values from `post` table of `user_id` column
```php
Post::query()->distinct('user_id');
```
#### Calculating Sum
Sum up sales where title is "maiores":
```php
Product::query()
->where('title', '=', 'maiores')
->sum('price');
```
#### Total Sales by Category
Calculate total sales per category by grouping results:
```php
return Product::query()
->select(['category_id', 'SUM(price * quantity) as total_sales'])
->groupBy('category_id')
->get();
```
### increment()
Sometimes, we need to `increment` a specific column. In this case, you can use `increment` as shown below. The example below will `increment` the post by 1.
```php
$post = Post::find(1);
$post->increment('views'); // default increment by 1
$post->increment('views',10); // increment by 10
```
Increment a post's views count by 1 while updating the timestamp and modifier:
```php
$post->increment('views', 1, [
'updated_at' => date('Y-m-d H:i:s'),
'modified_by' => Auth::id()
]);
```
### decrement()
Sometimes, we need to `decrement` a specific column. In this case, you can use `decrement` as shown below. The example below will `decrement` the post by 1.
```php
$post = Post::find(1);
$post->decrement('views'); // default decrement by 1
$post->decrement('views',10); // decrement by 10
```
Increment a post's views count by 1 while updating the timestamp and modifier:
```php
$post->decrement('views', 1, [
'updated_at' => date('Y-m-d H:i:s'),
'modified_by' => Auth::id()
]);
```
## Transform Eloquent Collection
You can transform a fetched Eloquent collection using the `map` function. This allows you to modify or format the data before returning it.
Here’s an example of how to extract and return only the name attribute from each user:
```php
return User::all()->map(function ($item) {
return [
'name' => $item->name
];
});
```
This approach is useful when you need to customize the response structure while working with Eloquent collections.
## Pagination
Pagination is a powerful feature that allows you to retrieve and display large datasets in smaller, manageable chunks. In Doppar, you can easily paginate your query results using the `paginate()` method. This method automatically handles the logic for splitting data into pages, making it ideal for scenarios like displaying user lists, logs, or any other dataset that requires.
### Example:
To paginate a list of users sorted by their id in descending order, you can use the following query:
```php
orderBy('id', 'desc')->paginate();
return view('user', [
'data' => $users
]);
}
}
```
### 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)
{{ $user->id }}
{{ $user->name }}
{{ $user->username }}
{{ $user->email }}
@endforeach
{!! paginator($data)->links() !!} // "Previous" and "Next" buttons, along with page numbers.
{!! paginator($data)->linkWithJumps() !!} // "Previous" and "Next" buttons, along with page jump options.
```
### 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/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(['name' => 'Mahedi']);
$post = Post::create(['user_id' => $user->id, 'title' => 'First Post']);
});
```
### 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.
## Manual 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()
->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.
## Database Operations with `DB` Facade
The Doppar Framework provides a powerful and elegant DB Facade under the namespace `Phaseolies\Support\Facades\DB`. This facade offers a fluent and expressive interface to interact with your database, allowing you to perform a variety of operations such as `querying`, `inserting`, `updating`, `deleting`, and handling transactions or stored `procedures` with ease.
### Basic Database Operation
Get a list of all tables in the database
```php
echo DB::getTables(); // Returns an array of all table names in the connected database.
```
Check if a specific table exists
```php
echo DB::tableExists('user'); // Returns true if the user table exists, otherwise false.
```
Get the column names of a given table
```php
echo DB::getTableColumns('user');
```
Get the table name associated with a model instance
```php
DB::getTable(new User()); // Useful for dynamically retrieving the table name from a model class instance.
```
Get the current active PDO connection instance
```php
return DB::getConnection(); // Returns the raw PDO connection object used internally.
```
### Querying the Database using `query()`
Doppar provides `query()` function and using it, you can pass raw sql and execute it. See the basic example. Get a single row from the user table
```php
DB::query("SELECT * FROM user WHERE name = ?", ['Kasper Snider'])->fetch();
```
Executes a query and returns the first matching row as an associative array.
Get all rows that match a query
```php
DB::query("SELECT * FROM user WHERE name = ?", ['Kasper Snider'])->fetchAll();
```
### 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 `executeProcedure` method to run your store procedure. See the basic example
```php
// Get nested results (original behavior)
$nestedResults = DB::executeProcedure('sp_GetAllUsers')->all();
```
Get flattened first result set
```php
$users = DB::executeProcedure('sp_GetAllUsers')->flatten();
```
Calls a procedure and flattens the first result set into a simple array.
Get first row only
```php
$firstUser = DB::executeProcedure('sp_GetUserById', [1])->first();
```
Get the second result set (index 1) by passing 123 parameter
```php
$stats = DB::executeProcedure('sp_GetUserWithStats', [123])->resultSet(1);
```
Useful when a stored procedure returns multiple result sets. This fetches the one at index 1.
### With Multiple Params
```php
$totalUsers = 0;
$results = DB::executeProcedure(
'sp_GetPaginatedUsers',
[1, 10, 'active', 'created_at', 'DESC'],
[&$totalUsers]
)->all();
// $results[0] contains user data
// $totalUsers contains total count
```
### Executing View
Doppar provides `executeView` method to run your `view`. See the basic example
```php
$stats = DB::executeView('vw_user_statistics');
```
### View with WHERE Conditions
You can pass `where` condition in your custom view like
```php
// 2. View with single WHERE condition
$nyUsers = DB::executeView(
'vw_user_locations',
['state' => 'New York']
);
// Equivalent to: SELECT * FROM vw_user_locations WHERE state = 'New York'
// 3. View with multiple WHERE conditions
$premiumNyUsers = DB::executeView(
'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::executeView(
'vw_recent_orders',
['status' => 'completed'],
[':min_amount' => 100] // Additional parameters
);
// Equivalent to:
// SELECT * FROM vw_recent_orders
// WHERE status = 'completed' AND amount > :min_amount
```
## Middleware
Middleware acts as a bridge between a request and a response, allowing you to filter or modify incoming requests before they reach the controller. It is useful for authentication, logging, and request modification.
Doppar supports two types of middleware
* Global Middleware
* Route Middleware
##### Global Middleware
Global middleware applies to all routes automatically. It is executed on every request, ensuring consistent behavior across the application.
##### Route Middleware
Route middleware is applied to specific routes, giving you more control over which requests are affected. You can assign middleware to a route or a group of routes as needed.
## Route Middleware
We can define multiple route middleware. To define route middleware, just update the `App\Http\Kernel.php` file's `$routeMiddleware` array as like below
#### Create New Middleware
Doppar has command line interface to create a new middleware. Doppar has `make:middleware` command to create a new middleware.
```
php pool make:middleware Authenticate
```
Then this command will create a new `Authenticate` for you located inside `App\Http\Authenticate` directory
```php
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
];
```
And update your route like:
```php
middleware('auth');
```
The `ProfileController` `index` method is now protected by the auth middleware. Update your middleware configuration to ensure authentication is required before accessing this method.
```php
to('/login');
}
}
```
## Global Middleware
We can register multiple global middleware. To register global middleware, just update the `App\Http\Kernel.php` file's `$middleware` array.
#### Create New Middleware
Doppar has command line interface to create a new middleware. Doppar has `make:middleware` command to create a new middleware.
```
php pool make:middleware CorsMiddlware
```
Then this command will create a new `CorsMiddleware` for you located inside `App\Http\Middleware` directory
```php
## Middleware Params
We can define multiple route middleware parameters. To define route middleware, add a `:` after the middleware name. If there are multiple parameters, separate them with a `,` comma. See the example
```php
middleware(['auth:admin,editor,publisher', 'is_subscribed:premium']);
```
* In this example:
* The auth middleware receives three parameters: `admin`, `editor`, and `publisher`.
* The `is_subscribed` middleware receives one parameter: `premium`.
#### Accept Parameters in Middleware
In the middleware class, define the handle method and accept the parameters as function arguments:
```php
## CSRF Protectection
Cross-site request forgery (CSRF) attacks are a type of security threat where unauthorized actions are executed on behalf of an authenticated user without their knowledge. Fortunately, Doppar provides robust built-in protection to safeguard your application against such attacks.
Doppar simplifies CSRF protection by automatically generating a unique CSRF token for every active user session. This token acts as a secure identifier to ensure that requests made to the application are genuinely coming from the authenticated user. The token is stored in the user's session and is regenerated whenever the session is refreshed, making it virtually impossible for malicious actors to replicate or misuse it.
You can access the current session's CSRF token either through the request's session data or by using the `csrf_token()` helper function. This seamless integration ensures that your application remains secure while requiring minimal effort on your part. With Doppar, you can focus on building your application with confidence, knowing that CSRF protection is handled efficiently in the background.
```php
use Phaseolies\Http\Request;
Route::get('/token', function (Request $request) {
$token = $request->session()->token();
$token = csrf_token();
// ...
});
```
Anytime you define a "POST" HTML form in your application, you should include a hidden CSRF _token field in the form so that the CSRF protection middleware can validate the request. For convenience, you may use the @csrf Blade directive to generate the hidden token input field:
```php
@csrf
```
## Controllers
Rather than defining all request-handling logic as closures in route files, you can use controller classes to organize related functionality. Controllers centralize request handling, making your code more structured and maintainable. For example, a UserController can manage user-related actions like displaying, creating, updating, and deleting users. By default, controllers are stored in the `app/Http/Controllers` directory.
To quickly generate a new controller, you may run the `make:controller` Pool command. By default, all of the controllers for your application are stored in the `app/Http/Controllers` directory
```
php pool make:controller UserController
php pool make:controller User/UserController // create controller inside User directory
```
Let's take a look at an example of a basic controller. A controller may have any number of public methods which will respond to incoming HTTP requests:
```php
User::find($id)
]);
}
}
```
Once you have written a controller class and method, you may define a route to the controller method like so:
```php
use Phaseolies\Support\Facades\Route;
use App\Http\Controllers\UserController;
Route::get('/user/{id}', [UserController::class, 'show']);
```
### Invokable Controllers
Doppar also support invokable controllers. You can call it single action controller also. To create a single action controller, need to pass the `--invok` option before create a controller.
```
php pool make:controller ProductController --invok
```
Now this command will create a invokable controller for you. For invokable controller, route defination will be like
```php
Route::get('/invoke_me', ProductController::class);
```
Now `ProductController` __invoke method automatically will be injected by Doppar container. But remember
> **⚠️ Warning:** Constructor dependency injection won't work for __invokable controllers
>
## Request
Doppar's Phaseolies\Http\Request class provides an object-oriented way to interact with the current HTTP request being handled by your application as well as retrieve the input, cookies, and files that were submitted with the request.
#### Accessing the Request Data
To access request data, Doppar has some default built in method.
Doppar provides a powerful `Request` class to handle incoming HTTP requests. This class offers a wide range of methods to access and manipulate request data, making it easy to build robust web applications.
#### HTML Form Request Data
These methods are used to access data submitted through HTML forms (e.g., POST, GET).
* **Accessing All Data:**
* `$request->all()`: Returns an array of all request data.
* **Accessing Specific Field Values:**
* `$request->name`: Retrieves the value of the "name" field.
* `$request->input('name')`: Equivalent to `$request->name`, using the `input()` method.
* `$request->get('name')`: Equivalent to previous one
* **Retrieving Data with Exclusions:**
* `$request->except('name')`: Retrieves all data except the "name" field.
* `$request->except(['name', 'age'])`: Retrieves data excluding "name" and "age".
* **Retrieving Specific Fields Only:**
* `$request->only('name')`: Retrieves only the "name" field.
* `$request->only(['name', 'age'])`: Retrieves only "name" and "age" fields.
* **Checking Field Existence:**
* `$request->has('name')`: Returns `true` if the "name" field exists, `false` otherwise.
* **Accessing Validation Results:**
* `$request->passed()`: Retrieves data that passed validation (if applied).
* `$request->failed()`: Retrieves data that failed validation (if applied).
* **Accessing Session Results:**
* `$request->session()->token()`: Retrieves csrf token data.
* `$request->session()->get('key')`: Get the session data that is stored in `key`
* `$request->session()->put('key','value')`: Set the session data that is will be stored in `key`
* `$request->session()->flush()`: To delete session data
#### Server Request Information
These methods provide access to server-related request information.
* **Client Information:**
* `$request->ip()`: Retrieves the client's IP address.
* `$request->userAgent()`: Retrieves the user agent string (browser/device info).
* `$request->referer()`: Retrieves the referer URL (where the request came from).
* **Headers:**
* `$request->headers()`: Retrieves all request headers as an array.
* `$request->header('key')`: Retrieves the value of a specific header by key.
* `$request->headers->get('host')`: Retrieves the value of a specific header by key.
* `$request->headers->set('key','value')`: Set the value to the header.
* **Request Details:**
* `$request->scheme()`: Retrieves the request scheme (e.g., "http" or "https").
* `$request->isSecure()`: Returns `true` if the request is using HTTPS, `false` otherwise.
* `$request->isAjax()`: Returns `true` if the request is an AJAX request, `false` otherwise.
* `$request->isJson()`: Returns `true` if the request expects a JSON response, `false` otherwise.
* `$request->contentType()`: Retrieves the content type of the request (e.g., "application/json").
* `$request->contentLength()`: Retrieves the content length of the request body.
* `$request->method()`: Retrieves the HTTP method used (e.g., GET, POST).
* `$request->query()`: Retrieves all query parameters (GET data).
* `$request->url()`: Retrieves the full URL of the request.
* `$request->host()`: Retrieves the host name (e.g., "example.com").
* `$request->server()`: Retrieves all server variables as an array.
* `$request->server->get('key')`: Get the server- by key name
* `$request->uri()`: Retrieves the request URI (e.g., "/path/to/resource").
* **Cookies:**
* `$request->cookie()`: Retrieves all cookies sent with the request.
* `$request->cookies->get('key')`: Get the cookie by key name
#### Authentication
This method is used to access authenticated user data.
* `$request->auth()`: Retrieves the authenticated user data.
* `$request->user()`: Retrieves the authenticated user data.
#### File Uploads
These methods handle file uploads from HTML forms.
* **Accessing the File Object:**
* `$image = $request->file('file')`: `'file'` is the HTML form input name.
* **Retrieving File Information:**
* If `$file = $request->file('file')`:
* If `$file->isValid()`:
* `$file->getClientOriginalName()`: Retrieves the original file name.
* `$file->getClientOriginalPath()`: Retrieves the temporary file path.
* `$file->getClientOriginalType()`: Retrieves the MIME type.
* `$file->getClientOriginalSize()`: Retrieves the file size in bytes.
* `$file->getClientOriginalExtension()`: Retrieves the file extension (e.g., "jpg", "png").
* `$file->generateUniqueName()`: Generate a unique name for the uploaded file
* `$file->isMimeType(string|array $mimeType)`: Checks if the uploaded file is of a specific MIME type
* `$file->isImage()`: Check if the uploaded file is an image.
* `$file->isVideo()`: Check if the uploaded file is a video.
* `$file->isDocument()`: Check if the uploaded file is a document.
* `$file->move(string $destination, ?string $fileName = null)`: Moves the uploaded file to a new location.
* `$file->getMimeTypeByFileInfo()`: Get the file's mime type by using the fileinfo extension.
* `$file->getError()`: Gets the error code of the uploaded file.
##### Example
```php
// Accessing the uploaded file object
$image = $request->file('file'); // 'file' is the HTML form input name
// Retrieving file information
if ($file = $request->file('file')) {
if ($file->isValid()) {
$file->getClientOriginalName(); // Retrieves the original file name
$file->getClientOriginalPath(); // Retrieves the temporary file path
$file->getClientOriginalType(); // Retrieves the MIME type
$file->getClientOriginalExtension(); // Retrieves the file extension like jpg, png
$file->getClientOriginalSize(); // Retrieves the file size in bytes
}
}
```
#### Global `request()` Helper
The `request()` helper function in Doppar provides a convenient and globally accessible way to retrieve data from the current HTTP request. It’s an alias for accessing the `Phaseolies\Http\Request` instance without needing to inject or typehint it.
### Basic Usage
```php
request('key', 'default');
```
- Retrieves the value of key from the current request.
- Returns 'default' if the key does not exist.
### Example:
```php
$name = request('name', 'Guest'); // Returns the 'name' from the request or 'Guest' if not set
```
You can do the same thing using `request()` object like
```php
$name = request()->input('name', 'Guest'); // as request object
```
## Response
All routes and controllers should return a response to be sent back to the user's browser. Doppar provides several different ways to return responses. The most basic response is returning a string from a route or controller. The framework will automatically convert the string into a full HTTP response:
```php
Route::get('/', function () {
return 'Hello World';
});
```
In addition to returning strings from your routes and controllers, you may also return arrays.
```php
Route::get('/', function () {
return [1, 2, 3];
});
```
### Collection
You can also return collection like
```php
Route::get('/', function () {
return $collection = collect([1, 2, 3, 4]);
return $collection->count();
});
```
### Response Objects
Typically, you won't just be returning simple strings or arrays from your route actions. Instead, you will be returning full `Phaseolies\Http\Response` instances.
```php
Route::get('/', function () {
return response()->text("Hello World", 200);
});
```
### Eloquent Models and Collections
For Eloquent, Doppar usage its own Eloquent Model and Collection. So you can return Eloquent collection data as `Phaseolies\Http\Response`, Doppar automatically convert this data as collection
```php
use App\Models\User;
Route::get('/user', function () {
return User::all();
});
```
### Attaching Headers to Responses
Keep in mind that most response methods are chainable, allowing for the fluent construction of response instances. For example, you may use the `setHeader` method to add a series of headers to the response before sending it back to the user:
```php
return response()->text("Hello World", 200)
->setHeader('Content-Type', 'text/plain')
->setHeader('X-Header-One', 'Header Value')
->setHeader('X-Header-Two', 'Header Value');
```
Or, you may use the withHeaders method to specify an array of headers to be added to the response:
```php
return response()->text("Hello World", 200)
->withHeaders([
'Content-Type' => 'text/plain',
'X-Header-One' => 'Header Value',
'X-Header-Two' => 'Header Value',
]);
```
You also use the `Response` object as the controller method params and get the same response like
```php
text("Hello World", 200)
->withHeaders([
'Content-Type' => 'text/plain',
'X-Header-One' => 'Header Value',
'X-Header-Two' => 'Header Value',
]);
}
}
```
You can also return response like this, directly using `response()` helper.
```php
return response($content, $status, $head