diff --git a/src/Connection.php b/src/Connection.php index f824599..7a0b4e9 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -27,6 +27,7 @@ * @method Results aggregate(string $function, array $wheres, array $options, array $columns) * @method Results distinct(array $wheres, array $options, array $columns, bool $includeDocCount = false) * @method Results find(array $wheres, array $options, array $columns) + * @method Results getDocument(string $id) * @method Results save(array $data, string $refresh) * @method array insertBulk(array $data, bool $returnData = false, string|null $refresh = false) * @method Results multipleAggregate(array $functions, array $wheres, array $options, string $column) diff --git a/src/DSL/Bridge.php b/src/DSL/Bridge.php index 2d1e8af..42b83d6 100644 --- a/src/DSL/Bridge.php +++ b/src/DSL/Bridge.php @@ -238,6 +238,35 @@ public function processSearch($searchParams, $searchOptions, $wheres, $opts, $fi return $this->_returnSearch($params, __FUNCTION__); } + /** + * @throws QueryException + */ + public function processGetDocument($id): Results + { + $params = [ + 'index' => $this->index, + 'id' => $id, + ]; + + return $this->_returnDocument($params, __FUNCTION__); + } + + /** + * @throws QueryException + */ + protected function _returnDocument($params, $source): Results + { + + $process = []; + try { + $process = $this->client->get($params); + } catch (Exception $e) { + $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__)); + } + + return $this->_sanitizeGetResponse($process, $params, $this->_queryTag($source)); + } + /** * @throws QueryException */ @@ -1179,6 +1208,28 @@ private function _sanitizeSearchResponse($response, $params, $queryTag) return $this->_return($data, $meta, $params, $queryTag); } + private function _sanitizeGetResponse($response): Results + { + + //Fake the Meta Response + $meta['took'] = 0; + $meta['timed_out'] = false; + $meta['total'] = 1; + $meta['max_score'] = 0; + $meta['shards'] = []; + + $response = $response->asArray(); + $data = [...$response['_source']]; + $data['_index'] = $response['_index']; + $data['_id'] = $response['_id']; + $data['_meta']['_index'] = $response['_index']; + $data['_meta']['_id'] = $response['_id']; + + //Hook in to the general return statement + return $this->_return([$data], $meta, [], ''); + + } + private function _sanitizeDistinctResponse($response, $columns, $includeDocCount): array { $keys = []; diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php index 123d9c1..b6342a0 100644 --- a/src/Eloquent/Builder.php +++ b/src/Eloquent/Builder.php @@ -8,11 +8,14 @@ use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Database\ConnectionInterface; use Illuminate\Database\Eloquent\Builder as BaseEloquentBuilder; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Pagination\Cursor; use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\Paginator; use Illuminate\Support\Collection; use PDPhilip\Elasticsearch\Collection\ElasticCollection; +use PDPhilip\Elasticsearch\DSL\exceptions\QueryException; +use PDPhilip\Elasticsearch\Exceptions\DocumentFoundException; use PDPhilip\Elasticsearch\Exceptions\MissingOrderException; use PDPhilip\Elasticsearch\Helpers\QueriesRelationships; use PDPhilip\Elasticsearch\Pagination\SearchAfterPaginator; @@ -199,6 +202,37 @@ public function firstOrCreate(array $attributes = [], array $values = []): Model return $this->create(array_merge($attributes, $values)); } + public function documentOrNew(string $id): Model + { + try { + $d = $this->getConnection()->getDocument($id); + //We use hydrate for the model, but we get the first result. + return $this->hydrate($d->data)->first(); + } catch (QueryException $e){ + // Create a new model instance but set the ID since we are looking for a specific ID here. + $newModel = $this->newModelInstance(); + $newModel['_id'] = $id; + return $newModel; + } + } + + /** + * @param string $id + * + * @return Model + * @throws ModelNotFoundException + */ + public function documentOrFail(string $id): Model + { + try { + $d = $this->getConnection()->getDocument($id); + //We use hydrate for the model, but we get the first result. + return $this->hydrate($d->data)->first(); + } catch (\Exception $e){ + throw new ModelNotFoundException($e->getMessage()); + } + } + private function _instanceBuilder(array $attributes = []) { $instance = clone $this; diff --git a/src/Eloquent/Docs/ModelDocs.php b/src/Eloquent/Docs/ModelDocs.php index b4ec1ba..504af89 100644 --- a/src/Eloquent/Docs/ModelDocs.php +++ b/src/Eloquent/Docs/ModelDocs.php @@ -53,6 +53,8 @@ * @method static $this whereRegex(string $column, string $regex) * @method static $this whereNestedObject(string $column, Callable $callback, string $scoreType = 'avg') * @method static $this whereNotNestedObject(string $column, Callable $callback, string $scoreType = 'avg') + * @method static $this documentOrNew(string $id) + * @method static $this documentOrFail(string $id) * @method static $this firstOrCreate(array $attributes, array $values = []) * @method static $this firstOrCreateWithoutRefresh(array $attributes, array $values = []) * @method static $this orderBy(string $column, string $direction = 'asc') diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 934d500..731b941 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -173,7 +173,36 @@ test('Find Or Fail', function () { $this->expectException(ModelNotFoundException::class); Product::findOrFail('51c33d8981fec6813e00000a'); +}); + +test('Document Or New', function () { + $product = Product::documentOrNew('51c33d8981fec6813e00000a'); + expect($product->exists)->toBe(false) + ->and($product['_id'])->toBe('51c33d8981fec6813e00000a'); + + $product['name'] = 'John Doe'; + $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.'; + $product['in_stock'] = 35; + $product->save(); + + $getDocument = Product::documentOrNew('51c33d8981fec6813e00000a'); + expect($getDocument->exists)->toBe(true)->and($product['_id'])->toBe('51c33d8981fec6813e00000a'); +}); +test('Document Or Fail', function () { + $this->expectException(ModelNotFoundException::class); + Product::documentOrFail('51c33d8981fec6813e00000a'); +}); + +test('Document Or Fail (Found)', function () { + $product = new Product; + $product['name'] = 'John Doe'; + $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.'; + $product['in_stock'] = 24; + $product['_id'] = '51c33d8981fec6813e00000a'; + $product->save(); + $getDocument = Product::documentOrFail('51c33d8981fec6813e00000a'); + expect($getDocument->exists)->toBe(true)->and($product['_id'])->toBe('51c33d8981fec6813e00000a'); }); test('Create', function () {