laravel-mongodb
laravel-mongodb copied to clipboard
EmbedsOne Relationship do not save / create / update in db
- Laravel-mongodb Version:
"jenssegers/mongodb": "^3.9",
"laravel/lumen-framework": "^9.0"
- PHP Version:
"php": "^8.0",
- Database Driver & Version:
"mongodb/mongodb": "^1.12.0",
MongoDB shell version 4.4.13
libmongoc bundled version 1.21.1
libmongocrypt bundled version 1.3.2
mongodb-community version 4.4
Description:
EmbedsOne Relationship do not save / create / update in db. Tested all the methods in README.md #embedsone-relationship or have I missed something.
Steps to reproduce
# @ database/migrations/create_suppliers_collection.php
use Illuminate\Database\Migrations\Migration;
use Jenssegers\Mongodb\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
protected $connection = 'mongodb';
public function up()
{
Schema::create('suppliers', function (Blueprint $collection) {
$collection->unique(['email']);
});
}
public function down()
{
Schema::dropIfExists('suppliers', function (Blueprint $collection) {
$collection->dropIndex(['email']);
});
}
};
# @ app/Models/Supplier.php
declare(strict_types=1);
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model;
use Jenssegers\Mongodb\Relations\EmbedsOne;
class Supplier extends Model
{
protected $fillable = [
'address',
'email',
];
public function addresses(): EmbedsOne
{
return $this->embedsOne(SupplierAddress::class);
}
}
# @ app/Models/SupplierAddress.php
declare(strict_types=1);
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model;
class SupplierAddress extends Model
{
protected $fillable = [
'streetAddress',
];
}
# @ tests/SeederTest.php
declare(strict_types=1);
namespace Tests;
use App\Models\Model;
use App\Models\Supplier;
use App\Models\SupplierAddress;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
class SeederTest extends TestCase
{
protected $data_source = [
'email' => '[email protected]',
// ! 'address' => 'street, 1',
'address' => [
'streetAddress' => 'street, 1',
'dirt' => 'do not save on mongodb',
],
'dirt' => 'do not save on mongodb',
];
public function test_that_inserts_and_updates_an_embeds_one()
{
DB::collection('suppliers')->delete();
# for supplier prepare only fillable fields & fill without address
$new_supplier = new Supplier();
$data_s_fillable = $new_supplier->getFillable();
if (count($data_s_fillable) > 0) {
$data_s_clean = array_intersect_key($this->data_source, array_flip($data_s_fillable));
}
$new_supplier->fill(
Arr::except($data_s_clean, ['address'])
);
# create supplier without address (clean)
$supplier = Supplier::create($new_supplier->toArray());
# for supplier->address prepare only fillable fields & fill
$new_supplier_address = new SupplierAddress();
$data_a_fillable = $new_supplier_address->getFillable();
if (count($data_a_fillable) > 0) {
$data_a_clean = array_intersect_key($this->data_source['address'], array_flip($data_a_fillable));
}
$new_supplier_address->fill(
$data_a_clean
);
/**
* Save supplier->address (clean)
* @see https://github.com/jenssegers/laravel-mongodb#embedsone-relationship
* @example
* $author = $book->author()->save(
* new Author(['name' => 'John Doe'])
* );
*/
$supplier->address()->save($new_supplier_address->toArray());
# get supplier from db
$stored_supplier = Supplier::first();
$this->assertEquals(
$this->data_source['address']['streetAddress'],
$stored_supplier->address()->streetAddress
);
}
public function test_that_inserts_and_updates_similiar_an_embeds_one()
{
DB::collection('suppliers')->delete();
# for supplier prepare only fillable fields & fill without address
$new_supplier = new Supplier();
$data_s_fillable = $new_supplier->getFillable();
if (count($data_s_fillable) > 0) {
$data_s_clean = array_intersect_key($this->data_source, array_flip($data_s_fillable));
}
$new_supplier->fill(
Arr::except($data_s_clean, ['address'])
);
# create supplier without address (clean)
$supplier = Supplier::create($new_supplier->toArray());
# for supplier->address prepare only fillable fields & fill
$new_supplier_address = new SupplierAddress();
$data_a_fillable = $new_supplier_address->getFillable();
if (count($data_a_fillable) > 0) {
$data_a_clean = array_intersect_key($this->data_source['address'], array_flip($data_a_fillable));
}
$new_supplier_address->fill(
$data_a_clean
);
/**
* Save supplier->address similar (clean)
* @see https://github.com/jenssegers/laravel-mongodb#embedsone-relationship
* @example
* $author =
* $book->author()
* ->create(['name' => 'John Doe']);
*/
$supplier->address()->create($new_supplier_address->toArray());
# get supplier from db
$stored_supplier = Supplier::first();
$this->assertEquals(
$this->data_source['address']['streetAddress'],
$stored_supplier->address()->streetAddress
);
}
public function test_that_updates_an_embeds_one()
{
DB::collection('suppliers')->delete();
# for supplier prepare only fillable fields & fill without address
$new_supplier = new Supplier();
$data_s_fillable = $new_supplier->getFillable();
if (count($data_s_fillable) > 0) {
$data_s_clean = array_intersect_key($this->data_source, array_flip($data_s_fillable));
}
$new_supplier->fill(
Arr::except($data_s_clean, ['address'])
);
# create supplier without address (clean)
$supplier = Supplier::create($new_supplier->toArray());
# for supplier->address prepare only fillable fields & fill
$new_supplier_address = new SupplierAddress();
$data_a_fillable = $new_supplier_address->getFillable();
if (count($data_a_fillable) > 0) {
$data_a_clean = array_intersect_key($this->data_source['address'], array_flip($data_a_fillable));
}
$new_supplier_address->fill(
$data_a_clean
);
/**
* Save supplier->address similar (clean)
* @see https://github.com/jenssegers/laravel-mongodb#embedsone-relationship
* @example
* $author = $book->author;
*
* $author->name = 'Jane Doe';
* $author->save();
*/
$supplier_address = $supplier->address;
$supplier_address->streetAddress = $new_supplier_address->toArray()['streetAddress'];
$supplier_address->save();
$this->assertEquals(
$this->data_source['address']['streetAddress'],
$supplier->address()->streetAddress
);
}
}
Expected behaviour
Save in db and get embebed one data.
PASS Tests\SeederTest
✓ that inserts and updates an embeds one
✓ that inserts and updates similar an embeds one
✓ that updates an embeds one
Actual behaviour
Do not save in db and can't get embebed one data.
FAIL Tests\SeederTest
⨯ that inserts and updates an embeds one
⨯ that inserts and updates similar an embeds one
⨯ that updates an embeds one
Logs:
• Tests\SeederTest > that inserts and updates an embeds one
Failed asserting that null matches expected 'street, 1'.
at tests/SeederTest.php:67
63▕ $stored_supplier = Supplier::first();
64▕
65▕ $this->assertEquals(
66▕ $data_source['address']['streetAddress'],
➜ 67▕ $stored_supplier->address()->streetAddress
68▕ );
69▕ }
70▕
71▕ public function test_that_inserts_and_updates_similiar_an_embeds_one()
• Tests\SeederTest > that inserts and updates similiar an embeds one
MongoDB\Driver\Exception\BulkWriteException
E11000 duplicate key error collection: foo.suppliers index: name_1 dup key: { name: null }
at vendor/mongodb/mongodb/src/Operation/InsertOne.php:123
119▕
120▕ $bulk = new Bulk($this->createBulkWriteOptions());
121▕ $insertedId = $bulk->insert($this->document);
122▕
➜ 123▕ $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());
124▕
125▕ return new InsertOneResult($writeResult, $insertedId);
126▕ }
127▕
+16 vendor frames
17 tests/SeederTest.php:118
Jenssegers\Mongodb\Eloquent\Model::__call("create")
• Tests\SeederTest > that updates an embeds one
LogicException
App\Models\Supplier::address must return a relationship instance.
at vendor/illuminate/database/Eloquent/Concerns/HasAttributes.php:549
545▕ '%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?', static::class, $method
546▕ ));
547▕ }
548▕
➜ 549▕ throw new LogicException(sprintf(
550▕ '%s::%s must return a relationship instance.', static::class, $method
551▕ ));
552▕ }
553▕
+3 vendor frames
4 tests/SeederTest.php:177
Illuminate\Database\Eloquent\Model::__get("address")
Hi @beeards, you can try this package!
Hi @SanaviaNicolas, your package looks good!
But I'm trying to update embedded models with jenssegers/laravel-mongodb package. It's supposed to uses exactly the same methods as original Laravel classes.
Hi @beeards, this package has some bugs on embeds relationships and they no longer be maintained. Laravel Mongo Auto Sync provides a better support for MongoDB relationships and all the methods are based on eloquent methods. Check our documentation.
Hi @SanaviaNicolas! I just migrated all to mysql, because I found out, and it seems that it will not be maintained (in short-mid term? 🤷), as you said.
* I've been reading the documentation and reviewing the code of Laravel Mongo Auto Sync and looks good, it has a lot of work behind, I will keep it in mind for other projects. IMO maybe some files need a revision (e.g.: Traits/ModelAdditionalMethod.php), I guess it's because it's being used in other projects.
@GromNaN According to the tests, it is fixed now.