Spot
Spotは「Simple PHP ORM」として、PHPアプリケーションのための軽量でシンプルなObject-Relational Mapping(ORM)ライブラリです。Doctrine ORMの重厚さに対するオルタナティブとして設計され、Active RecordとDataMapperパターンの両方をサポートしながら、学習コストの低い直感的なAPIを提供します。マイグレーション機能、関係性管理、クエリビルダー等のモダンなORM機能を軽量なパッケージで実現し、中小規模のPHPプロジェクトに最適な選択肢となっています。
GitHub概要
spotorm/spot2
Spot v2.x DataMapper built on top of Doctrine's Database Abstraction Layer
トピックス
スター履歴
ライブラリ
Spot
概要
Spotは「Simple PHP ORM」として、PHPアプリケーションのための軽量でシンプルなObject-Relational Mapping(ORM)ライブラリです。Doctrine ORMの重厚さに対するオルタナティブとして設計され、Active RecordとDataMapperパターンの両方をサポートしながら、学習コストの低い直感的なAPIを提供します。マイグレーション機能、関係性管理、クエリビルダー等のモダンなORM機能を軽量なパッケージで実現し、中小規模のPHPプロジェクトに最適な選択肢となっています。
詳細
Spot 2025年版はPHP 8.0以降に対応し、モダンPHPの機能(型宣言、属性、enum等)を活用したより安全で表現力豊かなデータベースプログラミングを提供します。シンプルなEntityクラス定義、柔軟なクエリビルダー、自動的なスキーママイグレーション、リレーションシップ管理により、開発者の生産性を大幅に向上させます。LaravelのEloquentほど高機能ではありませんが、DoctrineほどWeb複雑でもない、「ちょうど良い」レベルの機能セットを提供し、Composer管理による簡単な導入と最小限の設定で利用開始できます。
主な特徴
- 軽量設計: 最小限の依存関係とフットプリント
- 直感的API: 学習コストの低いシンプルなインターフェース
- マイグレーション: スキーマ変更の自動管理機能
- リレーションシップ: 一対一、一対多、多対多の関係サポート
- クエリビルダー: 柔軟で読みやすいクエリ構築
- データ検証: エンティティレベルでのバリデーション機能
メリット・デメリット
メリット
- DoctrineやEloquentに比べて軽量で学習コストが低い
- シンプルで直感的なAPIによる開発効率の向上
- 自動マイグレーション機能によるスキーマ管理の簡素化
- Active RecordとDataMapperの使い分けによる柔軟性
- 最小限の設定で素早いプロジェクト開始が可能
- Composerエコシステムとの自然な統合
デメリット
- LaravelのEloquentほど高機能ではない
- 大規模プロジェクトでの実績やエコシステムが限定的
- 複雑なクエリや高度なORM機能は他のライブラリに劣る
- コミュニティサイズが小さく情報やプラグインが少ない
- エンタープライズレベルの機能(キャッシュ等)は別途実装が必要
- パフォーマンス最適化の選択肢が限定的
参考ページ
書き方の例
セットアップ
// composer.json
{
"require": {
"vlucas/spot2": "^2.3",
"php": ">=8.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
# インストール
composer install
<?php
// config/database.php
use Spot\Locator;
use Spot\Config;
// データベース設定
$cfg = new Config();
// SQLite設定例
$cfg->addConnection('default', [
'dbname' => __DIR__ . '/../database/app.sqlite',
'driver' => 'pdo_sqlite'
]);
// MySQL設定例
$cfg->addConnection('mysql', [
'driver' => 'pdo_mysql',
'host' => 'localhost',
'dbname' => 'spot_demo',
'user' => 'root',
'password' => '',
'charset' => 'utf8mb4'
]);
// PostgreSQL設定例
$cfg->addConnection('postgresql', [
'driver' => 'pdo_pgsql',
'host' => 'localhost',
'dbname' => 'spot_demo',
'user' => 'postgres',
'password' => '',
'charset' => 'utf8'
]);
$spot = new Locator($cfg);
return $spot;
基本的な使い方
<?php
namespace App\Entity;
use Spot\EntityInterface;
use Spot\MapperInterface;
use Spot\Entity;
// ユーザーエンティティ
class User extends Entity
{
protected static $table = 'users';
public static function fields()
{
return [
'id' => ['type' => 'integer', 'primary' => true, 'autoincrement' => true],
'name' => ['type' => 'string', 'required' => true],
'email' => ['type' => 'string', 'required' => true, 'unique' => true],
'age' => ['type' => 'integer', 'default' => 0],
'active' => ['type' => 'boolean', 'default' => true],
'created_at' => ['type' => 'datetime', 'value' => new \DateTime()],
'updated_at' => ['type' => 'datetime', 'value' => new \DateTime()]
];
}
public static function relations(MapperInterface $mapper, EntityInterface $entity)
{
return [
'posts' => $mapper->hasMany($entity, 'App\Entity\Post', 'user_id'),
'profile' => $mapper->hasOne($entity, 'App\Entity\UserProfile', 'user_id'),
];
}
// カスタムメソッド
public function getFullInfo()
{
return sprintf('%s (%s) - Age: %d', $this->name, $this->email, $this->age);
}
// バリデーション
public static function validate($data)
{
$errors = [];
if (empty($data['name'])) {
$errors['name'] = 'Name is required';
}
if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Valid email is required';
}
if (isset($data['age']) && ($data['age'] < 0 || $data['age'] > 150)) {
$errors['age'] = 'Age must be between 0 and 150';
}
return $errors;
}
}
// 投稿エンティティ
class Post extends Entity
{
protected static $table = 'posts';
public static function fields()
{
return [
'id' => ['type' => 'integer', 'primary' => true, 'autoincrement' => true],
'title' => ['type' => 'string', 'required' => true],
'content' => ['type' => 'text'],
'user_id' => ['type' => 'integer', 'required' => true],
'published' => ['type' => 'boolean', 'default' => false],
'published_at' => ['type' => 'datetime'],
'created_at' => ['type' => 'datetime', 'value' => new \DateTime()],
'updated_at' => ['type' => 'datetime', 'value' => new \DateTime()]
];
}
public static function relations(MapperInterface $mapper, EntityInterface $entity)
{
return [
'user' => $mapper->belongsTo($entity, 'App\Entity\User', 'user_id'),
'comments' => $mapper->hasMany($entity, 'App\Entity\Comment', 'post_id'),
];
}
// スコープメソッド
public static function published($mapper)
{
return $mapper->where(['published' => true]);
}
public function publish()
{
$this->published = true;
$this->published_at = new \DateTime();
$this->updated_at = new \DateTime();
}
}
// ユーザープロフィールエンティティ
class UserProfile extends Entity
{
protected static $table = 'user_profiles';
public static function fields()
{
return [
'id' => ['type' => 'integer', 'primary' => true, 'autoincrement' => true],
'user_id' => ['type' => 'integer', 'required' => true, 'unique' => true],
'bio' => ['type' => 'text'],
'website' => ['type' => 'string'],
'location' => ['type' => 'string'],
'birth_date' => ['type' => 'date'],
'avatar_url' => ['type' => 'string'],
'created_at' => ['type' => 'datetime', 'value' => new \DateTime()],
'updated_at' => ['type' => 'datetime', 'value' => new \DateTime()]
];
}
public static function relations(MapperInterface $mapper, EntityInterface $entity)
{
return [
'user' => $mapper->belongsTo($entity, 'App\Entity\User', 'user_id'),
];
}
}
// 基本操作サービス
class UserService
{
private $spot;
public function __construct($spot)
{
$this->spot = $spot;
}
// スキーマ作成
public function createSchema()
{
$userMapper = $this->spot->mapper('App\Entity\User');
$postMapper = $this->spot->mapper('App\Entity\Post');
$profileMapper = $this->spot->mapper('App\Entity\UserProfile');
// テーブル作成
$userMapper->migrate();
$postMapper->migrate();
$profileMapper->migrate();
echo "Database schema created successfully\n";
}
// ユーザー作成
public function createUser($name, $email, $age = null)
{
$data = compact('name', 'email', 'age');
// バリデーション
$errors = User::validate($data);
if (!empty($errors)) {
throw new \InvalidArgumentException('Validation failed: ' . implode(', ', $errors));
}
$userMapper = $this->spot->mapper('App\Entity\User');
// 重複チェック
$existingUser = $userMapper->first(['email' => $email]);
if ($existingUser) {
throw new \InvalidArgumentException('Email already exists');
}
$user = $userMapper->create($data);
if ($user) {
echo "User created: {$user->getFullInfo()}\n";
return $user;
}
throw new \RuntimeException('Failed to create user');
}
// 全ユーザー取得
public function getAllUsers()
{
$userMapper = $this->spot->mapper('App\Entity\User');
return $userMapper->all()->order(['name' => 'ASC']);
}
// ID指定でユーザー取得
public function getUserById($id)
{
$userMapper = $this->spot->mapper('App\Entity\User');
return $userMapper->get($id);
}
// ユーザー更新
public function updateUser($id, $data)
{
$errors = User::validate($data);
if (!empty($errors)) {
throw new \InvalidArgumentException('Validation failed: ' . implode(', ', $errors));
}
$userMapper = $this->spot->mapper('App\Entity\User');
$user = $userMapper->get($id);
if (!$user) {
throw new \InvalidArgumentException('User not found');
}
// メール重複チェック(自分以外)
if (isset($data['email']) && $data['email'] !== $user->email) {
$existingUser = $userMapper->first(['email' => $data['email']]);
if ($existingUser) {
throw new \InvalidArgumentException('Email already exists');
}
}
$data['updated_at'] = new \DateTime();
$result = $userMapper->update($user, $data);
if ($result) {
echo "User updated successfully\n";
return $userMapper->get($id); // 更新後のデータを返す
}
throw new \RuntimeException('Failed to update user');
}
// ユーザー削除
public function deleteUser($id)
{
$userMapper = $this->spot->mapper('App\Entity\User');
$user = $userMapper->get($id);
if (!$user) {
throw new \InvalidArgumentException('User not found');
}
$result = $userMapper->delete($user);
if ($result) {
echo "User deleted successfully\n";
return true;
}
throw new \RuntimeException('Failed to delete user');
}
// 検索機能
public function searchUsers($query)
{
$userMapper = $this->spot->mapper('App\Entity\User');
return $userMapper->where(['name :like' => "%{$query}%"])
->orWhere(['email :like' => "%{$query}%"])
->order(['name' => 'ASC']);
}
// 年齢範囲でフィルタ
public function getUsersByAgeRange($minAge, $maxAge)
{
$userMapper = $this->spot->mapper('App\Entity\User');
return $userMapper->where(['age :gte' => $minAge])
->where(['age :lte' => $maxAge])
->order(['age' => 'ASC', 'name' => 'ASC']);
}
}
// 使用例
function demonstrateBasicOperations()
{
$spot = require __DIR__ . '/config/database.php';
$userService = new UserService($spot);
try {
// スキーマ作成
$userService->createSchema();
// ユーザー作成
$user1 = $userService->createUser('Alice Smith', '[email protected]', 25);
$user2 = $userService->createUser('Bob Johnson', '[email protected]', 30);
$user3 = $userService->createUser('Charlie Brown', '[email protected]', 35);
// 全ユーザー取得
echo "\n=== All Users ===\n";
$users = $userService->getAllUsers();
foreach ($users as $user) {
echo $user->getFullInfo() . "\n";
}
// 特定ユーザー取得
$user = $userService->getUserById($user1->id);
if ($user) {
echo "\n=== User by ID ===\n";
echo "Found: " . $user->getFullInfo() . "\n";
}
// ユーザー更新
$updatedUser = $userService->updateUser($user1->id, [
'name' => 'Alice Johnson',
'email' => '[email protected]',
'age' => 26
]);
echo "\n=== Updated User ===\n";
echo $updatedUser->getFullInfo() . "\n";
// 検索
echo "\n=== Search Results ===\n";
$searchResults = $userService->searchUsers('Alice');
foreach ($searchResults as $user) {
echo "Found: " . $user->getFullInfo() . "\n";
}
// 年齢範囲フィルタ
echo "\n=== Users by Age Range ===\n";
$ageFilteredUsers = $userService->getUsersByAgeRange(25, 35);
foreach ($ageFilteredUsers as $user) {
echo $user->getFullInfo() . "\n";
}
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
リレーションシップとクエリ
<?php
// コメントエンティティ
class Comment extends Entity
{
protected static $table = 'comments';
public static function fields()
{
return [
'id' => ['type' => 'integer', 'primary' => true, 'autoincrement' => true],
'post_id' => ['type' => 'integer', 'required' => true],
'user_id' => ['type' => 'integer', 'required' => true],
'content' => ['type' => 'text', 'required' => true],
'approved' => ['type' => 'boolean', 'default' => false],
'created_at' => ['type' => 'datetime', 'value' => new \DateTime()],
'updated_at' => ['type' => 'datetime', 'value' => new \DateTime()]
];
}
public static function relations(MapperInterface $mapper, EntityInterface $entity)
{
return [
'post' => $mapper->belongsTo($entity, 'App\Entity\Post', 'post_id'),
'user' => $mapper->belongsTo($entity, 'App\Entity\User', 'user_id'),
];
}
}
// ブログサービス(リレーション使用例)
class BlogService
{
private $spot;
public function __construct($spot)
{
$this->spot = $spot;
}
// 投稿作成
public function createPost($userId, $title, $content)
{
$userMapper = $this->spot->mapper('App\Entity\User');
$user = $userMapper->get($userId);
if (!$user) {
throw new \InvalidArgumentException('User not found');
}
$postMapper = $this->spot->mapper('App\Entity\Post');
$post = $postMapper->create([
'title' => $title,
'content' => $content,
'user_id' => $userId,
'published' => false
]);
if ($post) {
echo "Post created: {$post->title} by {$user->name}\n";
return $post;
}
throw new \RuntimeException('Failed to create post');
}
// ユーザーの投稿取得
public function getUserPosts($userId)
{
$postMapper = $this->spot->mapper('App\Entity\Post');
return $postMapper->where(['user_id' => $userId])
->order(['created_at' => 'DESC']);
}
// 投稿とユーザー情報の結合取得
public function getPostsWithAuthors()
{
$postMapper = $this->spot->mapper('App\Entity\Post');
return $postMapper->all()
->with('user')
->order(['created_at' => 'DESC']);
}
// 公開済み投稿取得
public function getPublishedPosts()
{
$postMapper = $this->spot->mapper('App\Entity\Post');
return $postMapper->where(['published' => true])
->with('user')
->order(['published_at' => 'DESC']);
}
// 投稿公開
public function publishPost($postId)
{
$postMapper = $this->spot->mapper('App\Entity\Post');
$post = $postMapper->get($postId);
if (!$post) {
throw new \InvalidArgumentException('Post not found');
}
$post->publish();
$result = $postMapper->save($post);
if ($result) {
echo "Post published: {$post->title}\n";
return $post;
}
throw new \RuntimeException('Failed to publish post');
}
// コメント追加
public function addComment($postId, $userId, $content)
{
$postMapper = $this->spot->mapper('App\Entity\Post');
$userMapper = $this->spot->mapper('App\Entity\User');
$post = $postMapper->get($postId);
$user = $userMapper->get($userId);
if (!$post) {
throw new \InvalidArgumentException('Post not found');
}
if (!$user) {
throw new \InvalidArgumentException('User not found');
}
$commentMapper = $this->spot->mapper('App\Entity\Comment');
$comment = $commentMapper->create([
'post_id' => $postId,
'user_id' => $userId,
'content' => $content,
'approved' => false
]);
if ($comment) {
echo "Comment added by {$user->name} on post: {$post->title}\n";
return $comment;
}
throw new \RuntimeException('Failed to add comment');
}
// 投稿のコメント取得
public function getPostComments($postId)
{
$commentMapper = $this->spot->mapper('App\Entity\Comment');
return $commentMapper->where(['post_id' => $postId])
->with('user')
->order(['created_at' => 'ASC']);
}
// 複雑なクエリ:人気ユーザー(投稿数順)
public function getPopularUsers($limit = 10)
{
$userMapper = $this->spot->mapper('App\Entity\User');
// SpotでJOINクエリを実行
$query = $userMapper->query("
SELECT u.*, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
ORDER BY post_count DESC
LIMIT {$limit}
");
return $query;
}
// 統計情報取得
public function getBlogStatistics()
{
$userMapper = $this->spot->mapper('App\Entity\User');
$postMapper = $this->spot->mapper('App\Entity\Post');
$commentMapper = $this->spot->mapper('App\Entity\Comment');
$stats = [
'total_users' => $userMapper->all()->count(),
'total_posts' => $postMapper->all()->count(),
'published_posts' => $postMapper->where(['published' => true])->count(),
'total_comments' => $commentMapper->all()->count(),
'approved_comments' => $commentMapper->where(['approved' => true])->count(),
];
return $stats;
}
}
// 使用例
function demonstrateRelationships()
{
$spot = require __DIR__ . '/config/database.php';
$userService = new UserService($spot);
$blogService = new BlogService($spot);
try {
// コメントテーブル作成
$commentMapper = $spot->mapper('App\Entity\Comment');
$commentMapper->migrate();
// ユーザー作成
$user = $userService->createUser('John Doe', '[email protected]', 30);
// 投稿作成
$post = $blogService->createPost($user->id, 'My First Post', 'This is the content of my first post.');
// 投稿公開
$publishedPost = $blogService->publishPost($post->id);
// コメント追加
$comment = $blogService->addComment($post->id, $user->id, 'Great post!');
// 公開済み投稿とユーザー情報取得
echo "\n=== Published Posts with Authors ===\n";
$postsWithAuthors = $blogService->getPublishedPosts();
foreach ($postsWithAuthors as $post) {
echo "Post: {$post->title} by {$post->user->name}\n";
}
// 投稿のコメント取得
echo "\n=== Post Comments ===\n";
$comments = $blogService->getPostComments($post->id);
foreach ($comments as $comment) {
echo "Comment by {$comment->user->name}: {$comment->content}\n";
}
// 統計情報
echo "\n=== Blog Statistics ===\n";
$stats = $blogService->getBlogStatistics();
foreach ($stats as $key => $value) {
echo ucfirst(str_replace('_', ' ', $key)) . ": {$value}\n";
}
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
高度な機能とカスタマイゼーション
<?php
// カスタムマッパー
class UserMapper extends \Spot\Mapper
{
// カスタムスコープ
public function active()
{
return $this->where(['active' => true]);
}
public function byAge($age)
{
return $this->where(['age' => $age]);
}
public function adults()
{
return $this->where(['age :gte' => 18]);
}
// カスタムクエリメソッド
public function findByEmailDomain($domain)
{
return $this->where(['email :like' => "%@{$domain}"]);
}
public function getUsersWithPostCount()
{
return $this->query("
SELECT u.*, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
ORDER BY u.name
");
}
// イベントフック
public function beforeSave($entity, $options)
{
if ($entity->isNew()) {
$entity->created_at = new \DateTime();
}
$entity->updated_at = new \DateTime();
return true;
}
public function afterSave($entity, $options)
{
// 保存後の処理(ログ出力、キャッシュクリア等)
echo "User saved: {$entity->name}\n";
return true;
}
}
// Userエンティティにカスタムマッパーを指定
class User extends Entity
{
protected static $table = 'users';
protected static $mapper = 'UserMapper';
// ... その他のメソッドは既存のまま
}
// 高度なサービスクラス
class AdvancedUserService
{
private $spot;
public function __construct($spot)
{
$this->spot = $spot;
}
// トランザクション処理
public function transferUserData($fromUserId, $toUserId)
{
$connection = $this->spot->connection();
try {
$connection->beginTransaction();
$userMapper = $this->spot->mapper('App\Entity\User');
$postMapper = $this->spot->mapper('App\Entity\Post');
$fromUser = $userMapper->get($fromUserId);
$toUser = $userMapper->get($toUserId);
if (!$fromUser || !$toUser) {
throw new \InvalidArgumentException('One or both users not found');
}
// 投稿を移行
$posts = $postMapper->where(['user_id' => $fromUserId]);
foreach ($posts as $post) {
$postMapper->update($post, ['user_id' => $toUserId]);
}
// 元のユーザーを削除
$userMapper->delete($fromUser);
$connection->commit();
echo "User data transferred successfully from User {$fromUserId} to User {$toUserId}\n";
return true;
} catch (\Exception $e) {
$connection->rollback();
throw new \RuntimeException("Transfer failed: " . $e->getMessage());
}
}
// バッチ処理
public function batchCreateUsers($usersData)
{
$userMapper = $this->spot->mapper('App\Entity\User');
$createdUsers = [];
$errors = [];
foreach ($usersData as $index => $userData) {
try {
$validationErrors = User::validate($userData);
if (!empty($validationErrors)) {
$errors[$index] = $validationErrors;
continue;
}
$user = $userMapper->create($userData);
if ($user) {
$createdUsers[] = $user;
} else {
$errors[$index] = 'Failed to create user';
}
} catch (\Exception $e) {
$errors[$index] = $e->getMessage();
}
}
return [
'created' => $createdUsers,
'errors' => $errors,
'success_count' => count($createdUsers),
'error_count' => count($errors)
];
}
// 条件付き更新
public function updateUsersByCondition($conditions, $updates)
{
$userMapper = $this->spot->mapper('App\Entity\User');
$users = $userMapper->where($conditions);
$updatedCount = 0;
$errors = [];
foreach ($users as $user) {
try {
$result = $userMapper->update($user, $updates);
if ($result) {
$updatedCount++;
}
} catch (\Exception $e) {
$errors[] = "Failed to update user {$user->id}: " . $e->getMessage();
}
}
return [
'updated_count' => $updatedCount,
'errors' => $errors
];
}
// データエクスポート
public function exportUsersToJson($conditions = [])
{
$userMapper = $this->spot->mapper('App\Entity\User');
$query = empty($conditions) ? $userMapper->all() : $userMapper->where($conditions);
$users = $query->with('posts', 'profile');
$exportData = [];
foreach ($users as $user) {
$userData = [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'age' => $user->age,
'active' => $user->active,
'created_at' => $user->created_at->format('Y-m-d H:i:s'),
'posts' => []
];
if ($user->posts) {
foreach ($user->posts as $post) {
$userData['posts'][] = [
'id' => $post->id,
'title' => $post->title,
'published' => $post->published,
'created_at' => $post->created_at->format('Y-m-d H:i:s')
];
}
}
if ($user->profile) {
$userData['profile'] = [
'bio' => $user->profile->bio,
'website' => $user->profile->website,
'location' => $user->profile->location
];
}
$exportData[] = $userData;
}
return json_encode($exportData, JSON_PRETTY_PRINT);
}
// キャッシュ機能(簡易版)
private $cache = [];
public function getUserWithCache($id)
{
if (isset($this->cache["user_{$id}"])) {
echo "Retrieved from cache: User {$id}\n";
return $this->cache["user_{$id}"];
}
$userMapper = $this->spot->mapper('App\Entity\User');
$user = $userMapper->get($id);
if ($user) {
$this->cache["user_{$id}"] = $user;
echo "Cached: User {$id}\n";
}
return $user;
}
public function clearUserCache($id = null)
{
if ($id) {
unset($this->cache["user_{$id}"]);
} else {
$this->cache = [];
}
}
}
// 使用例
function demonstrateAdvancedFeatures()
{
$spot = require __DIR__ . '/config/database.php';
$advancedService = new AdvancedUserService($spot);
try {
// バッチユーザー作成
$usersData = [
['name' => 'User 1', 'email' => '[email protected]', 'age' => 25],
['name' => 'User 2', 'email' => '[email protected]', 'age' => 30],
['name' => '', 'email' => 'invalid', 'age' => -5], // バリデーションエラー
['name' => 'User 4', 'email' => '[email protected]', 'age' => 35],
];
echo "=== Batch User Creation ===\n";
$batchResult = $advancedService->batchCreateUsers($usersData);
echo "Created: {$batchResult['success_count']}, Errors: {$batchResult['error_count']}\n";
if (!empty($batchResult['errors'])) {
foreach ($batchResult['errors'] as $index => $error) {
echo "Error at index {$index}: " . (is_array($error) ? implode(', ', $error) : $error) . "\n";
}
}
// 条件付き更新
echo "\n=== Conditional Update ===\n";
$updateResult = $advancedService->updateUsersByCondition(
['age :gte' => 30],
['active' => false]
);
echo "Updated {$updateResult['updated_count']} users\n";
// データエクスポート
echo "\n=== Data Export ===\n";
$jsonData = $advancedService->exportUsersToJson(['active' => true]);
echo "Export data (first 200 chars): " . substr($jsonData, 0, 200) . "...\n";
// キャッシュテスト
echo "\n=== Cache Test ===\n";
$user1 = $advancedService->getUserWithCache(1);
$user2 = $advancedService->getUserWithCache(1); // キャッシュから取得
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
// メイン実行
function main()
{
echo "=== Spot ORM Demo ===\n\n";
demonstrateBasicOperations();
echo "\n" . str_repeat("=", 50) . "\n\n";
demonstrateRelationships();
echo "\n" . str_repeat("=", 50) . "\n\n";
demonstrateAdvancedFeatures();
}
if (php_sapi_name() === 'cli') {
main();
}