dedsecteam17 / soft-mapper
A lightweight, powerful, and easy-to-use PHP ORM library for MySQL databases with advanced features including relationships, soft deletes, and automatic timestamps
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/dedsecteam17/soft-mapper
Requires
- php: >=5.6
- ext-pdo: *
- ext-pdo_mysql: *
This package is not auto-updated.
Last update: 2025-12-12 05:19:11 UTC
README
A lightweight, powerful, and easy-to-use PHP ORM (Object-Relational Mapping) library for MySQL databases. Soft-Mapper provides a clean and intuitive interface for database operations with built-in security features and advanced ORM capabilities.
🚀 What's New in v2.0
Version 2.0 brings 27 new methods and advanced ORM features that make Soft-Mapper comparable to Laravel's Eloquent:
- ✨ Automatic Timestamps - Auto-manage created_at/updated_at
- ✨ Soft Deletes - Mark records as deleted without removing them
- ✨ ORM Relationships - Define 1:1, 1:N, and N:N relationships (NEW!)
- ✨ Eager Loading - Load relationships efficiently to prevent N+1 queries (NEW!)
- ✨ Query Scopes - Reusable query constraints
- ✨ Batch Operations - Insert multiple records efficiently
- ✨ Transactions - Full transaction support
- ✨ Advanced Queries - whereIn, whereBetween, whereNull, JOINs
- ✨ Helper Methods - count(), exists(), first(), pluck()
- ✨ And much more! - See CHANGELOG.md for complete list
📚 Documentation:
- Quick Start Guide - Get started with new features
- ORM Relationships Guide - Complete guide to relationships (NEW!)
- Changelog - Complete list of changes
- Advanced Examples - Real-world usage examples
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- Configuration
- Usage
- Advanced Features
- API Reference
- Examples
- Security
- Troubleshooting
- Contributing
- License
- Author
Features
Core Features
- ✅ Simple and Intuitive API: Easy-to-understand method chaining interface
- ✅ Full CRUD Support: Create, Read, Update, and Delete operations
- ✅ Query Builder: Build complex SQL queries with simple PHP methods
- ✅ Security First: Built-in prepared statements prevent SQL injection
- ✅ Aggregate Functions: Support for COUNT, SUM, AVG, MIN, MAX, etc.
- ✅ Flexible Filtering: WHERE and HAVING clauses with multiple conditions
- ✅ Result Ordering: ORDER BY with ASC/DESC support
- ✅ Result Grouping: GROUP BY for aggregate queries
- ✅ Result Limiting: LIMIT clause for pagination
- ✅ Method Chaining: Chain multiple methods for complex queries
- ✅ PDO-Based: Uses PHP Data Objects for database abstraction
- ✅ Lightweight: Minimal dependencies, single-file implementation
Advanced Features ⭐ NEW
- ✨ Automatic Timestamps: Auto-manage created_at and updated_at columns
- ✨ Soft Deletes: Mark records as deleted without removing from database
- ✨ ORM Relationships: Define and query 1:1, 1:N, and N:N relationships
- ✨ Eager Loading: Load relationships efficiently with with()
- ✨ Relationship Methods: hasOne(), hasMany(), belongsTo(), belongsToMany()
- ✨ Pivot Table Operations: attach(), detach(), sync() for many-to-many
- ✨ Pagination with OFFSET: Full pagination support with limit and offset
- ✨ Custom Primary Keys: Support for non-'id' primary key columns
- ✨ Batch Operations: Insert multiple records efficiently with insertMany()
- ✨ Query Scopes: Define reusable query constraints
- ✨ JOIN Support: INNER JOIN, LEFT JOIN, RIGHT JOIN operations
- ✨ Advanced WHERE Clauses: whereIn, whereNotIn, whereBetween, whereNull
- ✨ Helper Methods: first(), count(), exists(), pluck()
- ✨ Transaction Support: Complete transaction management
- ✨ Raw Queries: Execute custom SQL when needed
- ✨ Chunking: Memory-efficient processing of large datasets
- ✨ Update or Create: Intelligent upsert operations
- ✨ Distinct Results: Get unique records only
Requirements
- PHP: Version 5.6 or higher (PHP 7.x or 8.x recommended)
- MySQL: Version 5.5 or higher
- PHP Extensions:
- PDO
- PDO_MySQL
Installation
Using Composer (Recommended)
Install via Composer:
composer require dedsecteam17/soft-mapper
Then include the autoloader in your project:
require_once 'vendor/autoload.php';
Manual Installation
- Clone this repository or download the files:
git clone https://github.com/DedSecTeam17/Soft-Mapper.git
cd Soft-Mapper
- Include the library in your project:
require_once 'path/to/SoftMapper.php';
Using as a Git Submodule
git submodule add https://github.com/DedSecTeam17/Soft-Mapper.git libs/soft-mapper
Then include in your code:
require_once __DIR__ . '/libs/soft-mapper/SoftMapper.php';
Quick Start
<?php // 1. Configure database connection require_once 'env.php'; // 2. Create a model require_once 'SoftMapper.php'; class User extends SoftMapper { public $table_name = "users"; public $columns = []; public function __construct() { parent::__construct(); } } // 3. Use the model $user = new User(); // Insert a new user $user->columns['name'] = 'John Doe'; $user->columns['email'] = 'john@example.com'; $user->insert(); // Fetch all users $all_users = $user->all()->getAll();
Configuration
Database Configuration
Edit env.php to configure your database connection:
<?php define('host', 'localhost'); // Database host define('dbname', 'your_database'); // Database name define('user', 'your_username'); // Database username define('password', 'your_password'); // Database password
Environment-Specific Configuration
For production environments, consider using environment variables:
<?php define('host', getenv('DB_HOST') ?: 'localhost'); define('dbname', getenv('DB_NAME') ?: 'softmapper'); define('user', getenv('DB_USER') ?: 'root'); define('password', getenv('DB_PASSWORD') ?: '');
Usage
Creating Models
Each model represents a database table. Extend the SoftMapper class:
<?php require_once 'SoftMapper.php'; class Post extends SoftMapper { public $table_name = "posts"; // Your table name public $columns = []; // Array to hold column values public function __construct() { parent::__construct(); } }
Basic CRUD Operations
Create (Insert)
Insert new records into the database:
$post = new Post(); $post->columns['title'] = 'My First Post'; $post->columns['body'] = 'This is the content of my post.'; $post->columns['author_id'] = 1; $post->columns['status'] = 'published'; $post->columns['created_at'] = date('Y-m-d H:i:s'); if ($post->insert()) { echo "Post created successfully!"; }
Read (Select)
Find by ID:
$post = new Post(); $result = $post->find(1); if ($result) { echo $result->title; }
Get All Records:
$post = new Post(); $all_posts = $post->all()->getAll(); foreach ($all_posts as $p) { echo $p->title . "<br>"; }
Get Single Record:
$post = new Post(); $single_post = $post->all()->where([['id', '=', 1]])->get();
Select Specific Columns:
$post = new Post(); $results = $post->select(['title', 'author_id', 'created_at'])->getAll();
Update
Update existing records:
$post = new Post(); $post->columns['title'] = 'Updated Title'; $post->columns['body'] = 'Updated content'; $post->columns['updated_at'] = date('Y-m-d H:i:s'); $post->update()->where([['id', '=', 1]])->execute();
Update Multiple Records:
$post = new Post(); $post->columns['status'] = 'archived'; $post->update() ->where([ ['created_at', '<', '2023-01-01', 'AND'], ['status', '=', 'draft'] ]) ->execute();
Delete
Delete records from the database:
$post = new Post(); $post->delete()->where([['id', '=', 1]])->execute();
Delete Multiple Records:
$post = new Post(); $post->delete() ->where([['status', '=', 'spam']]) ->execute();
Advanced Queries
WHERE Clauses
Simple WHERE:
$post = new Post(); $results = $post->all() ->where([['status', '=', 'published']]) ->getAll();
Multiple Conditions with AND:
$post = new Post(); $results = $post->all() ->where([ ['status', '=', 'published', 'AND'], ['author_id', '=', 5] ]) ->getAll();
Using Different Operators:
$post = new Post(); $results = $post->all() ->where([ ['views', '>', 100, 'AND'], ['created_at', '>=', '2024-01-01'] ]) ->getAll();
ORDER BY
Order Descending:
$post = new Post(); $results = $post->all() ->orderBy('created_at', 'DESC') ->getAll();
Order Ascending:
$post = new Post(); $results = $post->all() ->orderBy('title', 'ASC') ->getAll();
LIMIT
Limit Results:
$post = new Post(); $results = $post->all() ->limit(10) ->getAll();
Pagination Example:
$post = new Post(); $page = 2; $per_page = 10; $offset = ($page - 1) * $per_page; // Note: For offset, you may need to enhance the library $results = $post->all() ->orderBy('created_at', 'DESC') ->limit($per_page) ->getAll();
Combining Multiple Clauses
$post = new Post(); $results = $post->all() ->where([ ['status', '=', 'published', 'AND'], ['author_id', '=', 5] ]) ->orderBy('created_at', 'DESC') ->limit(5) ->getAll();
Aggregate Functions
COUNT
Count all records:
$post = new Post(); $result = $post->select([], 'COUNT', '*')->get(); echo "Total posts: " . $result->{'COUNT(*)'};
Count with GROUP BY:
$post = new Post(); $results = $post->select(['author_id'], 'COUNT', 'id') ->groupBy('author_id') ->getAll(); foreach ($results as $result) { echo "Author {$result->author_id} has {$result->{'COUNT(id)'}} posts<br>"; }
SUM, AVG, MIN, MAX
// Sum of all views $post = new Post(); $result = $post->select([], 'SUM', 'views')->get(); // Average views $result = $post->select([], 'AVG', 'views')->get(); // Maximum views $result = $post->select([], 'MAX', 'views')->get(); // Minimum views $result = $post->select([], 'MIN', 'views')->get();
HAVING Clause
Use HAVING with aggregate functions:
$post = new Post(); $results = $post->select(['author_id'], 'COUNT', 'id') ->groupBy('author_id') ->having([['COUNT(id)', '>', 5]]) ->getAll();
Advanced Features
Automatic Timestamps
Enable automatic timestamp management for created_at and updated_at columns:
class Post extends SoftMapper { public $table_name = "posts"; public $columns = []; // Enable automatic timestamps (default: true) protected $timestamps = true; public function __construct() { parent::__construct(); } } $post = new Post(); $post->columns['title'] = 'My Post'; $post->insert(); // created_at and updated_at are automatically set // Update also automatically updates updated_at $post->columns['title'] = 'Updated Title'; $post->update()->where([['id', '=', 1]])->execute(); // updated_at is automatically updated
Soft Deletes
Soft delete marks records as deleted without removing them from the database:
class Post extends SoftMapper { public $table_name = "posts"; public $columns = []; // Enable soft deletes protected $soft_deletes = true; public function __construct() { parent::__construct(); } } // Soft delete (sets deleted_at timestamp) $post = new Post(); $post->delete()->where([['id', '=', 1]])->execute(); // Get only soft-deleted records $trashed = $post->onlyTrashed()->getAll(); // Include soft-deleted in results $all = $post->withTrashed()->all()->getAll(); // Restore a soft-deleted record $post->restore()->where([['id', '=', 1]])->execute(); // Force delete (permanent) $post->delete(true)->where([['id', '=', 1]])->execute();
Custom Primary Keys
Override the default 'id' primary key:
class Post extends SoftMapper { public $table_name = "posts"; public $columns = []; // Custom primary key protected $primary_key = 'post_id'; public function __construct() { parent::__construct(); } } $post = new Post(); $result = $post->find(123); // Uses 'post_id' instead of 'id'
Pagination with OFFSET
Full pagination support:
$post = new Post(); $page = 2; $per_page = 10; $offset = ($page - 1) * $per_page; $results = $post->all() ->orderBy('created_at', 'DESC') ->limit($per_page) ->offset($offset) ->getAll();
Batch Insert
Insert multiple records efficiently:
$post = new Post(); $records = [ ['title' => 'Post 1', 'body' => 'Content 1', 'author_id' => 1], ['title' => 'Post 2', 'body' => 'Content 2', 'author_id' => 2], ['title' => 'Post 3', 'body' => 'Content 3', 'author_id' => 1], ]; $result = $post->insertMany($records);
Advanced WHERE Clauses
WHERE IN
$post = new Post(); $results = $post->all() ->whereIn('id', [1, 2, 3, 5, 8]) ->getAll();
WHERE NOT IN
$post = new Post(); $results = $post->all() ->whereNotIn('status', ['draft', 'archived']) ->getAll();
WHERE BETWEEN
$post = new Post(); $results = $post->all() ->whereBetween('views', 100, 1000) ->getAll();
WHERE NULL / WHERE NOT NULL
$post = new Post(); $with_featured = $post->all() ->whereNotNull('featured_image') ->getAll(); $without_category = $post->all() ->whereNull('category_id') ->getAll();
Query Scopes
Define reusable query constraints:
class Post extends SoftMapper { public $table_name = "posts"; public $columns = []; public function __construct() { parent::__construct(); // Define a 'published' scope $this->scope('published', function($query) { $query->where([['status', '=', 'published']]); }); // Define a 'popular' scope with parameters $this->scope('popular', function($query, $min_views = 1000) { $query->where([['views', '>', $min_views]]); }); } } // Use scopes $post = new Post(); $published = $post->all()->applyScope('published')->getAll(); $popular = $post->all()->applyScope('popular', 500)->getAll();
JOIN Operations
Perform table joins:
// INNER JOIN $post = new Post(); $results = $post->select(['posts.title', 'users.name']) ->join('users', 'posts.author_id', '=', 'users.id') ->getAll(); // LEFT JOIN $post = new Post(); $results = $post->select(['posts.*', 'categories.name']) ->leftJoin('categories', 'posts.category_id', '=', 'categories.id') ->getAll(); // RIGHT JOIN $post = new Post(); $results = $post->select(['posts.*', 'tags.name']) ->rightJoin('tags', 'posts.tag_id', '=', 'tags.id') ->getAll();
Helper Methods
first()
Get the first record:
$post = new Post(); $latest = $post->all() ->orderBy('created_at', 'DESC') ->first();
count()
Count records:
$post = new Post(); $total = $post->all()->count(); $published_count = $post->all()->where([['status', '=', 'published']])->count();
exists()
Check if records exist:
$post = new Post(); $has_posts = $post->all()->exists(); $has_published = $post->all()->where([['status', '=', 'published']])->exists();
pluck()
Get a single column's values:
$post = new Post(); $titles = $post->all()->pluck('title'); // Returns: ['Title 1', 'Title 2', 'Title 3']
Transaction Support
Manage database transactions:
$post = new Post(); try { $post->beginTransaction(); $post->columns['title'] = 'Post 1'; $post->insert(); $post->columns['title'] = 'Post 2'; $post->insert(); $post->commit(); echo "Transaction committed"; } catch (Exception $e) { $post->rollback(); echo "Transaction rolled back"; }
Raw Queries
Execute custom SQL:
$post = new Post(); $results = $post->raw( "SELECT status, COUNT(*) as count FROM posts WHERE created_at > :date GROUP BY status", ['date' => '2024-01-01'] );
Chunking
Process large datasets efficiently:
$post = new Post(); $post->all()->chunk(100, function($posts) { // Process 100 posts at a time foreach ($posts as $p) { // Process each post } });
Update or Create
Update existing record or create new one:
$post = new Post(); $result = $post->updateOrCreate( ['slug' => 'my-unique-post'], ['title' => 'My Unique Post', 'body' => 'Content', 'status' => 'published'] );
Distinct Results
Get unique records:
$post = new Post(); $unique_authors = $post->select(['author_id'])->distinct()->getAll();
Get Last Insert ID
$post = new Post(); $post->columns['title'] = 'New Post'; $post->insert(); $last_id = $post->lastInsertId(); echo "New post ID: " . $last_id;
ORM Relationships
Soft-Mapper now supports defining and querying relationships between models, making it easy to work with related data.
Defining Relationships
Define relationships in your model classes:
class User extends SoftMapper { public $table_name = "users"; // One-to-Many: User has many posts public function posts() { return $this->hasMany('Post', 'user_id', 'id'); } // One-to-One: User has one profile public function profile() { return $this->hasOne('UserProfile', 'user_id', 'id'); } } class Post extends SoftMapper { public $table_name = "posts"; // Belongs To: Post belongs to a user public function user() { return $this->belongsTo('User', 'user_id', 'id'); } // One-to-Many: Post has many comments public function comments() { return $this->hasMany('Comment', 'post_id', 'id'); } // Many-to-Many: Post belongs to many tags public function tags() { return $this->belongsToMany('Tag', 'post_tag', 'post_id', 'tag_id'); } }
Loading Relationships
Lazy Loading (load relationships as needed):
$user = new User(); $user_record = $user->find(1); // Load the posts relationship $user->loadRelation('posts', $user_record); // Access related data foreach ($user_record->posts as $post) { echo $post->title . "\n"; }
Eager Loading (load relationships efficiently for multiple records):
$post = new Post(); // Load posts with users and comments in one query $posts = $post->with(['user', 'comments'])->all()->getAll(); foreach ($posts as $p) { echo "Post: " . $p->title . "\n"; echo "Author: " . $p->user->name . "\n"; echo "Comments: " . count($p->comments) . "\n"; }
Many-to-Many Operations
Manage many-to-many relationships with pivot table operations:
$post = new Post(); // Attach a tag to a post $post->attach(1, 5, 'tags'); // Attach tag ID 5 to post ID 1 // Detach a tag from a post $post->detach(1, 'tags', 5); // Detach tag ID 5 from post ID 1 // Sync tags (replace all existing with new ones) $post->sync(1, [1, 2, 3], 'tags'); // Post ID 1 now has tags 1, 2, 3
Complete Example
// Create user and posts with relationships $user = new User(); $user->columns = ['name' => 'John Doe', 'email' => 'john@example.com']; $user->insert(); $user_id = $user->lastInsertId(); $post = new Post(); $post->columns = [ 'user_id' => $user_id, 'title' => 'My First Post', 'body' => 'This is my first post!', 'status' => 'published' ]; $post->insert(); $post_id = $post->lastInsertId(); // Attach tags to post $post->attach($post_id, 1, 'tags'); // Tag: PHP $post->attach($post_id, 2, 'tags'); // Tag: MySQL // Load post with all relationships $loaded_post = $post->with(['user', 'comments', 'tags'])->find($post_id); echo "Post: " . $loaded_post->title . "\n"; echo "Author: " . $loaded_post->user->name . "\n"; echo "Tags: " . count($loaded_post->tags) . "\n";
📚 For complete relationship documentation, see RELATIONSHIPS.md
API Reference
Core Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
all() |
None | $this |
Selects all records from table |
select() |
array $columns, string $aggregate, string $parameter |
$this |
Select specific columns with optional aggregate |
find() |
mixed $id |
object|null |
Find record by primary key |
insert() |
None | bool |
Insert new record using $columns array |
update() |
None | $this |
Update records (use with where()) |
delete() |
bool $force |
$this |
Delete records (supports soft delete) |
where() |
array $conditions |
$this |
Add WHERE conditions |
orderBy() |
string $column, string $direction |
$this |
Order results |
groupBy() |
string $column |
$this |
Group results |
having() |
array $conditions |
$this |
Add HAVING conditions |
limit() |
int $number |
$this |
Limit number of results |
offset() |
int $number |
$this |
Offset for pagination |
getAll() |
None | array |
Execute query and fetch all results |
get() |
None | object|null |
Execute query and fetch single result |
execute() |
None | bool |
Execute query (for UPDATE/DELETE) |
Advanced Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
withTrashed() |
None | $this |
Include soft deleted records |
onlyTrashed() |
None | $this |
Get only soft deleted records |
restore() |
None | $this |
Restore soft deleted records |
insertMany() |
array $records |
bool |
Insert multiple records at once |
whereIn() |
string $column, array $values |
$this |
WHERE IN clause |
whereNotIn() |
string $column, array $values |
$this |
WHERE NOT IN clause |
whereBetween() |
string $column, mixed $start, mixed $end |
$this |
WHERE BETWEEN clause |
whereNull() |
string $column |
$this |
WHERE NULL clause |
whereNotNull() |
string $column |
$this |
WHERE NOT NULL clause |
first() |
None | object|null |
Get the first record |
count() |
None | int |
Count records |
exists() |
None | bool |
Check if records exist |
pluck() |
string $column |
array |
Get single column values |
scope() |
string $name, callable $callback |
void |
Define a query scope |
applyScope() |
string $name, ...$args |
$this |
Apply a query scope |
beginTransaction() |
None | bool |
Start a database transaction |
commit() |
None | bool |
Commit a database transaction |
rollback() |
None | bool |
Rollback a database transaction |
raw() |
string $query, array $params |
mixed |
Execute raw SQL query |
lastInsertId() |
None | string |
Get last inserted ID |
chunk() |
int $size, callable $callback |
void |
Process results in chunks |
join() |
string $table, string $first, string $op, string $second, string $type |
$this |
Join tables |
leftJoin() |
string $table, string $first, string $op, string $second |
$this |
LEFT JOIN |
rightJoin() |
string $table, string $first, string $op, string $second |
$this |
RIGHT JOIN |
distinct() |
None | $this |
Get distinct records |
updateOrCreate() |
array $attributes, array $values |
bool |
Update existing or create new record |
Relationship Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
hasOne() |
string $class, string $foreign_key, string $local_key |
$this |
Define one-to-one relationship |
hasMany() |
string $class, string $foreign_key, string $local_key |
$this |
Define one-to-many relationship |
belongsTo() |
string $class, string $foreign_key, string $owner_key |
$this |
Define inverse relationship |
belongsToMany() |
string $class, string $pivot, string $foreign_key, string $related_key |
$this |
Define many-to-many relationship |
loadRelation() |
string $name, object $record |
object |
Load a relationship for a record |
with() |
array $relations |
$this |
Eager load relationships |
attach() |
mixed $id, mixed $related_id, string $relation, array $pivot_data |
bool |
Attach in many-to-many |
detach() |
mixed $id, string $relation, mixed $related_id |
bool |
Detach in many-to-many |
sync() |
mixed $id, array $related_ids, string $relation |
bool |
Sync many-to-many relationships |
Condition Array Format
For where() and having() methods:
[
['column_name', 'operator', 'value', 'logical_operator'],
['column_name', 'operator', 'value'] // Last condition doesn't need logical operator
]
Supported Operators: =, >, <, >=, <=, !=, <>
Logical Operators: AND, OR
Examples
See the example.php file for complete working examples. Here are some real-world scenarios:
Blog Post Management
<?php require_once 'Post.php'; $post = new Post(); // Get all published posts ordered by date $published_posts = $post->all() ->where([['status', '=', 'published']]) ->orderBy('created_at', 'DESC') ->getAll(); // Get popular posts (views > 1000) $popular_posts = $post->all() ->where([['views', '>', 1000]]) ->orderBy('views', 'DESC') ->limit(5) ->getAll(); // Count posts by status $post_counts = $post->select(['status'], 'COUNT', 'id') ->groupBy('status') ->getAll();
User Management
<?php class User extends SoftMapper { public $table_name = "users"; public $columns = []; public function __construct() { parent::__construct(); } } $user = new User(); // Register new user $user->columns['username'] = 'johndoe'; $user->columns['email'] = 'john@example.com'; $user->columns['password'] = password_hash('secret', PASSWORD_DEFAULT); $user->columns['created_at'] = date('Y-m-d H:i:s'); $user->insert(); // Find user by email $found_user = $user->all() ->where([['email', '=', 'john@example.com']]) ->get();
Security
SQL Injection Protection
Soft-Mapper uses PDO prepared statements for all database queries, which automatically protects against SQL injection attacks. User input is never directly concatenated into SQL queries.
// Safe from SQL injection $post = new Post(); $user_input = $_GET['id']; // Even malicious input is safe $result = $post->find($user_input);
Best Practices
- Never expose database credentials: Keep
env.phpoutside your web root or use environment variables - Validate user input: Even though SQL injection is prevented, validate data types and formats
- Hash passwords: Always use
password_hash()for storing passwords - Sanitize output: Use
htmlspecialchars()when displaying data in HTML - Use HTTPS: Encrypt data in transit
- Implement access controls: Check user permissions before database operations
Example with validation:
$post = new Post(); // Validate input $title = filter_var($_POST['title'], FILTER_SANITIZE_STRING); $body = filter_var($_POST['body'], FILTER_SANITIZE_STRING); if (strlen($title) > 0 && strlen($body) > 0) { $post->columns['title'] = $title; $post->columns['body'] = $body; $post->insert(); } else { echo "Invalid input"; }
Troubleshooting
Common Issues
1. PDO Connection Error
Error: SQLSTATE[HY000] [2002] No such file or directory
Solution: Check your database configuration in env.php. Ensure MySQL is running.
2. Table doesn't exist
Error: SQLSTATE[42S02]: Base table or view not found
Solution: Verify that $table_name in your model matches your actual database table name.
3. Column not found
Error: SQLSTATE[42S22]: Column not found
Solution: Ensure column names in $columns array match your database schema.
4. Class 'PDO' not found
Solution: Enable the PDO and PDO_MySQL extensions in your php.ini:
extension=pdo extension=pdo_mysql
Debug Mode
To see the generated SQL queries, you can temporarily add debugging to the SoftMapper class:
// In getAll(), get(), or execute() methods, add: var_dump($this->built_query); var_dump($this->query_columns_place_holder_array);
Getting Help
If you encounter issues:
- Check the examples section
- Review your database configuration
- Verify your table and column names
- Check PHP and MySQL versions meet requirements
- Open an issue on GitHub
Contributing
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes: Implement your feature or bug fix
- Test your changes: Ensure everything works as expected
- Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
Development Guidelines
- Follow PSR coding standards
- Add comments for complex logic
- Update documentation for new features
- Test with multiple PHP versions if possible
- Keep backward compatibility in mind
License
This project is open source and available under the MIT License. Feel free to use, modify, and distribute this software.
Author
Mohammed Elamin
- Created: December 2018
- GitHub: @DedSecTeam17
Star this Repository ⭐
If you find Soft-Mapper useful, please consider giving it a star on GitHub. It helps others discover the project!
Support
For questions, issues, or feature requests, please visit:
Happy Coding! 🚀