Sometimes you want to use Eloquent without a regular database table storing the data, so what do you do?
Recently, I wanted to load some Elasticsearch documents but I still wanted to take advantage of the comfortable features that Eloquent offers, such as toJson()
and mutators. I eventually found the awesomeness that is jenssegers/model which is essentially the same thing as Eloquent but without the database-related stuff.
All we need to do is to use a different base class when extending our model, and then declare our own find
-method. Shouldn't be too hard!
<?php
use \Jenssegers\Model\Model as EloquentLike;
/**
* Eloquent-like syntax for loading orders from Elasticsearch.
* Jens Segers base model gives us the essentials.
*/
class Order extends EloquentLike {
/**
* A static variable means that the cache will be shared
* between multiple instances of the Order class.
*/
protected static $orders = array();
/**
* Just like Eloquent, we can tell it to always include
* our "email" attribute when exporting to JSON or array
*/
protected $appends = array(
'email',
);
/**
* Load a specific document from Elasticsearch
*/
public static function find($ordernumber)
{
if(empty($ordernumber)) return FALSE;
// If we already fetched it once, lets just return the cached value
if(isset(static::$orders[$ordernumber])) return static::$orders[$ordernumber];
// Prep an Elasticsearch query
$searchParams['index'] = 'store';
$searchParams['type'] = 'order';
$searchParams['size'] = 1;
$searchParams['body'] = array(
'query' => array(
'simple_query_string' => array(
'query' => '"'.$ordernumber.'"',
'fields' => array('ordernumber'),
),
)
);
$result = Es::search($searchParams);
// Either the foreach contains one result..
foreach($result['hits']['hits'] as $hit)
{
// Prepare a filled Order model
$instance = new static;
static::unguard();
$instance->fill($hit['_source']);
static::reguard();
// Cache it before returning it
static::$orders[$ordernumber] = $instance;
return $instance;
}
// ..or the foreach is never run, meaning we didnt find anything.
static::$orders[$ordernumber] = FALSE;
return FALSE;
}
/**
* Example mutator to deal with legacy. You dont need this. :)
*/
public function getIdAttribute($value)
{
if($value) return $value;
return $this->entity_id; // Legacy property name
}
/**
* Figure out the customer's email and put it in a
* property that we guarantee is always correct.
*
* This method is called every time we use toJson() or toArray()
* because the "email" attribute is defined in our $appends property
*/
public function getEmailAttribute()
{
if(isset($this->entity_id))
{
// Legacy documents use this
$email = $this->customer_email;
}
else
{
$email = $this->customer['email'];
}
return $email;
}
}
Of course, you could replace the Elasticsearch functionality with whatever you want, like a remote API call or reading data from a CSV.
Now, thanks to the brilliance of science, we can do fancy stuff like:
$order = Order::find('12345');
if(!$order) die('No order with that ordernumber found :(');
echo 'Found order with id '.$order->id;
Hopefully this lets you save some time fetching these documents.