Skip to content

Commit

Permalink
Merge branch '4.2' into merge-4.1-into-4.2-1712583963596
Browse files Browse the repository at this point in the history
  • Loading branch information
GromNaN authored Apr 8, 2024
2 parents 2b0b5e6 + 2074da3 commit e82fe60
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 35 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/build-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ jobs:
- "8.2"
- "8.3"
laravel:
- "10.*"
- "10.*"
- "11.*"
include:
- php: "8.1"
laravel: "10.*"
mongodb: "5.0"
mode: "low-deps"
exclude:
- php: "8.1"
laravel: "11.*"

steps:
- uses: "actions/checkout@v4"
Expand Down Expand Up @@ -76,8 +80,7 @@ jobs:
restore-keys: "${{ matrix.os }}-composer-"

- name: "Install dependencies"
run: composer update --no-interaction $([[ "${{ matrix.mode }}" == low-deps ]] && echo ' --prefer-lowest --prefer-stable')

run: composer update --no-interaction $([[ "${{ matrix.mode }}" == low-deps ]] && echo ' --prefer-lowest')
- name: "Run tests"
run: "./vendor/bin/phpunit --coverage-clover coverage.xml"
env:
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.

## [unreleased]


## [4.2.0] - 2024-12-14

* Add support for Laravel 11 by @GromNaN in [#2735](https://github.com/mongodb/laravel-mongodb/pull/2735)
* Implement Model::createOrFirst() using findOneAndUpdate operation by @GromNaN in [#2742](https://github.com/mongodb/laravel-mongodb/pull/2742)

## [4.1.3] - 2024-03-05

* Fix the timezone of `datetime` fields when they are read from the database. By @GromNaN in [#2739](https://github.com/mongodb/laravel-mongodb/pull/2739)
Expand Down
14 changes: 6 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@
"require": {
"php": "^8.1",
"ext-mongodb": "^1.15",
"illuminate/support": "^10.0",
"illuminate/container": "^10.0",
"illuminate/database": "^10.30",
"illuminate/events": "^10.0",
"illuminate/support": "^10.0|^11",
"illuminate/container": "^10.0|^11",
"illuminate/database": "^10.30|^11",
"illuminate/events": "^10.0|^11",
"mongodb/mongodb": "^1.15"
},
"require-dev": {
"phpunit/phpunit": "^10.3",
"orchestra/testbench": "^8.0",
"orchestra/testbench": "^8.0|^9.0",
"mockery/mockery": "^1.4.4",
"doctrine/coding-standard": "12.0.x-dev",
"spatie/laravel-query-builder": "^5.6",
"phpstan/phpstan": "^1.10"
},
"minimum-stability": "dev",
"replace": {
"jenssegers/mongodb": "self.version"
},
Expand Down Expand Up @@ -66,9 +67,6 @@
"cs:fix": "phpcbf"
},
"config": {
"platform": {
"php": "8.1"
},
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
Expand Down
8 changes: 8 additions & 0 deletions docs/includes/framework-compatibility-laravel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@
:stub-columns: 1

* - {+odm-short+} Version
- Laravel 11.x
- Laravel 10.x
- Laravel 9.x

* - 4.2
- ✓
- ✓
-

* - 4.1
-
- ✓
-

* - 4.0
-
- ✓
-

2 changes: 1 addition & 1 deletion docs/quick-start.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ that connects to a MongoDB deployment.
.. tip::

You can download the complete web application project by cloning the
`laravel-quickstart <https://github.com/mongodb-university/laravel-quickstart>`__
`laravel-quickstart <https://github.com/mongodb-university/laravel-quickstart/>`__
GitHub repository.

.. button:: Next: Download and Install
Expand Down
11 changes: 8 additions & 3 deletions docs/quick-start/configure-mongodb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ Configure Your MongoDB Connection

// ...

.. step:: Add the Laravel MongoDB provider
.. step:: Add the MongoDB provider

Open the ``app.php`` file in the ``config`` directory and
add the following entry into the ``providers`` array:
Open the ``providers.php`` file in the ``bootstrap`` directory and add
the following entry into the array:

.. code-block::

MongoDB\Laravel\MongoDBServiceProvider::class,

.. tip::

To learn how to register the provider in Laravel 10.x, see
`Registering Providers <https://laravel.com/docs/10.x/providers#registering-providers>`__.

After completing these steps, your Laravel web application is ready to
connect to MongoDB.

Expand Down
14 changes: 7 additions & 7 deletions docs/quick-start/view-data.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ View MongoDB Data

public function show()
{
return view('browse_movies', [
'movies' => Movie::where('runtime', '<', 60)
->where('imdb.rating', '>', 8.5)
->orderBy('imdb.rating', 'desc')
->take(10)
->get()
]);
return view('browse_movies', [
'movies' => Movie::where('runtime', '<', 60)
->where('imdb.rating', '>', 8.5)
->orderBy('imdb.rating', 'desc')
->take(10)
->get()
]);
}

.. step:: Add a web route
Expand Down
17 changes: 14 additions & 3 deletions docs/quick-start/write-data.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ Write Data to MongoDB

.. step:: Add an API route that calls the controller function

Generate an API route file by running the following command:

.. code-block:: bash

php artisan install:api

.. tip::

Skip this step if you are using Laravel 10.x because the file that
the command generates already exists.

Import the controller and add an API route that calls the ``store()``
method in the ``routes/api.php`` file:

Expand All @@ -42,7 +53,7 @@ Write Data to MongoDB
// ...

Route::resource('movies', MovieController::class)->only([
'store'
'store'
]);


Expand All @@ -57,8 +68,8 @@ Write Data to MongoDB

class Movie extends Model
{
protected $connection = 'mongodb';
protected $fillable = ['title', 'year', 'runtime', 'imdb', 'plot'];
protected $connection = 'mongodb';
protected $fillable = ['title', 'year', 'runtime', 'imdb', 'plot'];
}

.. step:: Post a request to the API
Expand Down
2 changes: 1 addition & 1 deletion docs/retrieve.txt
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ documents.
Runtime: 95
IMDB Rating: 4
IMDB Votes: 9296
Plot: A sci-fi update of the famous 6th Century poem. In a beseiged land, Beowulf must
Plot: A sci-fi update of the famous 6th Century poem. In a besieged land, Beowulf must
battle against the hideous creature Grendel and his vengeance seeking mother.

.. _laravel-retrieve-one:
Expand Down
2 changes: 1 addition & 1 deletion src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ private static function lookupVersion(): string
try {
return self::$version = InstalledVersions::getPrettyVersion('mongodb/laravel-mongodb');
} catch (Throwable) {
// Ignore exceptions and return unknown version
return self::$version = 'error';
}
}

Expand Down
51 changes: 51 additions & 0 deletions src/Eloquent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@

use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use InvalidArgumentException;
use MongoDB\Driver\Cursor;
use MongoDB\Laravel\Collection;
use MongoDB\Laravel\Helpers\QueriesRelationships;
use MongoDB\Laravel\Internal\FindAndModifyCommandSubscriber;
use MongoDB\Model\BSONDocument;
use MongoDB\Operation\FindOneAndUpdate;

use function array_intersect_key;
use function array_key_exists;
use function array_merge;
use function collect;
Expand Down Expand Up @@ -183,6 +188,52 @@ public function raw($value = null)
return $results;
}

/**
* Attempt to create the record if it does not exist with the matching attributes.
* If the record exists, it will be returned.
*
* @param array $attributes The attributes to check for duplicate records
* @param array $values The attributes to insert if no matching record is found
*/
public function createOrFirst(array $attributes = [], array $values = []): Model
{
if ($attributes === []) {
throw new InvalidArgumentException('You must provide attributes to check for duplicates');
}

// Apply casting and default values to the attributes
// In case of duplicate key between the attributes and the values, the values have priority
$instance = $this->newModelInstance($values + $attributes);
$values = $instance->getAttributes();
$attributes = array_intersect_key($attributes, $values);

return $this->raw(function (Collection $collection) use ($attributes, $values) {
$listener = new FindAndModifyCommandSubscriber();
$collection->getManager()->addSubscriber($listener);

try {
$document = $collection->findOneAndUpdate(
$attributes,
// Before MongoDB 5.0, $setOnInsert requires a non-empty document.
// This is should not be an issue as $values includes the query filter.
['$setOnInsert' => (object) $values],
[
'upsert' => true,
'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
'typeMap' => ['root' => 'array', 'document' => 'array'],
],
);
} finally {
$collection->getManager()->removeSubscriber($listener);
}

$model = $this->model->newFromBuilder($document);
$model->wasRecentlyCreated = $listener->created;

return $model;
});
}

/**
* Add the "updated at" column to an array of values.
* TODO Remove if https://github.com/laravel/framework/commit/6484744326531829341e1ff886cc9b628b20d73e
Expand Down
23 changes: 22 additions & 1 deletion src/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace MongoDB\Laravel\Eloquent;

use BackedEnum;
use Carbon\CarbonInterface;
use DateTimeInterface;
use DateTimeZone;
Expand All @@ -23,6 +24,7 @@
use MongoDB\BSON\UTCDateTime;
use MongoDB\Laravel\Query\Builder as QueryBuilder;
use Stringable;
use ValueError;

use function array_key_exists;
use function array_keys;
Expand All @@ -40,10 +42,12 @@
use function is_string;
use function ltrim;
use function method_exists;
use function sprintf;
use function str_contains;
use function str_starts_with;
use function strcmp;
use function uniqid;
use function var_export;

abstract class Model extends BaseModel
{
Expand Down Expand Up @@ -695,7 +699,7 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
}

if ($this->isEnumCastable($key) && (! $castValue instanceof Arrayable)) {
$castValue = $castValue !== null ? $this->getStorableEnumValue($castValue) : null;
$castValue = $castValue !== null ? $this->getStorableEnumValueFromLaravel11($this->getCasts()[$key], $castValue) : null;
}

if ($castValue instanceof Arrayable) {
Expand All @@ -708,6 +712,23 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
return $attributes;
}

/**
* Duplicate of {@see HasAttributes::getStorableEnumValue()} for Laravel 11 as the signature of the method has
* changed in a non-backward compatible way.
*
* @todo Remove this method when support for Laravel 10 is dropped.
*/
private function getStorableEnumValueFromLaravel11($expectedEnum, $value)
{
if (! $value instanceof $expectedEnum) {
throw new ValueError(sprintf('Value [%s] is not of the expected enum type [%s].', var_export($value, true), $expectedEnum));
}

return $value instanceof BackedEnum
? $value->value
: $value->name;
}

/**
* Is a value a BSON type?
*
Expand Down
34 changes: 34 additions & 0 deletions src/Internal/FindAndModifyCommandSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace MongoDB\Laravel\Internal;

use MongoDB\Driver\Monitoring\CommandFailedEvent;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;

/**
* Track findAndModify command events to detect when a document is inserted or
* updated.
*
* @internal
*/
final class FindAndModifyCommandSubscriber implements CommandSubscriber
{
public bool $created;

public function commandFailed(CommandFailedEvent $event)
{
}

public function commandStarted(CommandStartedEvent $event)
{
}

public function commandSucceeded(CommandSucceededEvent $event)
{
$this->created = ! $event->getReply()->lastErrorObject->updatedExisting;
}
}
6 changes: 1 addition & 5 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -614,11 +614,7 @@ public function orderBy($column, $direction = 'asc')
return $this;
}

/**
* @param list{mixed, mixed}|CarbonPeriod $values
*
* @inheritdoc
*/
/** @inheritdoc */
public function whereBetween($column, iterable $values, $boolean = 'and', $not = false)
{
$type = 'between';
Expand Down
Loading

0 comments on commit e82fe60

Please sign in to comment.