Skip to content

Commit e6f79e1

Browse files
committed
fix conflicts
2 parents 20d6b4b + 24cbb80 commit e6f79e1

File tree

77 files changed

+1975
-123
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1975
-123
lines changed

.github/workflows/databases.yml

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -189,48 +189,48 @@ jobs:
189189
DB_CONNECTION: pgsql
190190
DB_PASSWORD: password
191191

192-
mssql:
193-
runs-on: ubuntu-20.04
194-
195-
services:
196-
sqlsrv:
197-
image: mcr.microsoft.com/mssql/server:2019-latest
198-
env:
199-
ACCEPT_EULA: Y
200-
SA_PASSWORD: Forge123
201-
ports:
202-
- 1433:1433
203-
204-
strategy:
205-
fail-fast: true
206-
207-
name: SQL Server 2019
208-
209-
steps:
210-
- name: Checkout code
211-
uses: actions/checkout@v3
212-
with:
213-
fetch-depth: 0
214-
215-
- name: Setup PHP
216-
uses: shivammathur/setup-php@v2
217-
with:
218-
php-version: 8.2
219-
extensions: dom, curl, libxml, mbstring, zip, pcntl, sqlsrv, pdo, pdo_sqlsrv, odbc, pdo_odbc
220-
tools: composer:v2
221-
coverage: none
222-
223-
- name: Install dependencies
224-
uses: nick-fields/retry@v2
225-
with:
226-
timeout_minutes: 5
227-
max_attempts: 5
228-
command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
229-
230-
- name: Execute tests
231-
run: vendor/bin/phpunit tests/Integration/Database
232-
env:
233-
DB_CONNECTION: sqlsrv
234-
DB_DATABASE: master
235-
DB_USERNAME: SA
236-
DB_PASSWORD: Forge123
192+
# mssql:
193+
# runs-on: ubuntu-20.04
194+
195+
# services:
196+
# sqlsrv:
197+
# image: mcr.microsoft.com/mssql/server:2019-latest
198+
# env:
199+
# ACCEPT_EULA: Y
200+
# SA_PASSWORD: Forge123
201+
# ports:
202+
# - 1433:1433
203+
204+
# strategy:
205+
# fail-fast: true
206+
207+
# name: SQL Server 2019
208+
209+
# steps:
210+
# - name: Checkout code
211+
# uses: actions/checkout@v3
212+
# with:
213+
# fetch-depth: 0
214+
215+
# - name: Setup PHP
216+
# uses: shivammathur/setup-php@v2
217+
# with:
218+
# php-version: 8.2
219+
# extensions: dom, curl, libxml, mbstring, zip, pcntl, sqlsrv, pdo, pdo_sqlsrv, odbc, pdo_odbc
220+
# tools: composer:v2
221+
# coverage: none
222+
223+
# - name: Install dependencies
224+
# uses: nick-fields/retry@v2
225+
# with:
226+
# timeout_minutes: 5
227+
# max_attempts: 5
228+
# command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
229+
230+
# - name: Execute tests
231+
# run: vendor/bin/phpunit tests/Integration/Database
232+
# env:
233+
# DB_CONNECTION: sqlsrv
234+
# DB_DATABASE: master
235+
# DB_USERNAME: SA
236+
# DB_PASSWORD: Forge123

docker-compose.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,32 @@ services:
2020
ports:
2121
- "3306:3306"
2222
restart: always
23+
# postgres:
24+
# image: postgres:15
25+
# environment:
26+
# POSTGRES_PASSWORD: "secret"
27+
# POSTGRES_DB: "forge"
28+
# ports:
29+
# - "5432:5432"
30+
# restart: always
31+
# mariadb:
32+
# image: mariadb:11
33+
# environment:
34+
# MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: "yes"
35+
# MARIADB_ROOT_PASSWORD: ""
36+
# MARIADB_DATABASE: "forge"
37+
# MARIADB_ROOT_HOST: "%"
38+
# ports:
39+
# - "3306:3306"
40+
# restart: always
41+
# mssql:
42+
# image: mcr.microsoft.com/mssql/server:2019-latest
43+
# environment:
44+
# ACCEPT_EULA: "Y"
45+
# SA_PASSWORD: "Forge123"
46+
# ports:
47+
# - "1433:1433"
48+
# restart: always
2349
redis:
2450
image: redis:7.0-alpine
2551
ports:

src/Illuminate/Broadcasting/Broadcasters/AblyBroadcaster.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ protected function formatChannels(array $channels)
206206
/**
207207
* Get the public token value from the Ably key.
208208
*
209-
* @return mixed
209+
* @return string
210210
*/
211211
protected function getPublicToken()
212212
{

src/Illuminate/Console/GeneratorCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ protected function promptForMissingArgumentsUsing()
498498
'Factory' => 'E.g. PostFactory',
499499
'Job' => 'E.g. ProcessPodcast',
500500
'Listener' => 'E.g. SendPodcastNotification',
501-
'Mail' => 'E.g. OrderShipped',
501+
'Mailable' => 'E.g. OrderShipped',
502502
'Middleware' => 'E.g. EnsureTokenIsValid',
503503
'Model' => 'E.g. Flight',
504504
'Notification' => 'E.g. InvoicePaid',

src/Illuminate/Database/Connection.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,12 +792,29 @@ protected function runQueryCallback($query, $bindings, Closure $callback)
792792
// message to include the bindings with SQL, which will make this exception a
793793
// lot more helpful to the developer instead of just the database's errors.
794794
catch (Exception $e) {
795+
if ($this->isUniqueConstraintError($e)) {
796+
throw new UniqueConstraintViolationException(
797+
$this->getName(), $query, $this->prepareBindings($bindings), $e
798+
);
799+
}
800+
795801
throw new QueryException(
796802
$this->getName(), $query, $this->prepareBindings($bindings), $e
797803
);
798804
}
799805
}
800806

807+
/**
808+
* Determine if the given database exception was caused by a unique constraint violation.
809+
*
810+
* @param \Exception $exception
811+
* @return bool
812+
*/
813+
protected function isUniqueConstraintError(Exception $exception)
814+
{
815+
return false;
816+
}
817+
801818
/**
802819
* Log a query in the connection's query log.
803820
*

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Illuminate\Database\Eloquent\Relations\Relation;
1515
use Illuminate\Database\Query\Builder as QueryBuilder;
1616
use Illuminate\Database\RecordsNotFoundException;
17+
use Illuminate\Database\UniqueConstraintViolationException;
1718
use Illuminate\Pagination\Paginator;
1819
use Illuminate\Support\Arr;
1920
use Illuminate\Support\Str;
@@ -554,7 +555,7 @@ public function firstOrNew(array $attributes = [], array $values = [])
554555
}
555556

556557
/**
557-
* Get the first record matching the attributes or create it.
558+
* Get the first record matching the attributes. If the record is not found, create it.
558559
*
559560
* @param array $attributes
560561
* @param array $values
@@ -566,9 +567,23 @@ public function firstOrCreate(array $attributes = [], array $values = [])
566567
return $instance;
567568
}
568569

569-
return tap($this->newModelInstance(array_merge($attributes, $values)), function ($instance) {
570-
$instance->save();
571-
});
570+
return $this->createOrFirst($attributes, $values);
571+
}
572+
573+
/**
574+
* Attempt to create the record. If a unique constraint violation occurs, attempt to find the matching record.
575+
*
576+
* @param array $attributes
577+
* @param array $values
578+
* @return \Illuminate\Database\Eloquent\Model|static
579+
*/
580+
public function createOrFirst(array $attributes = [], array $values = [])
581+
{
582+
try {
583+
return $this->withSavepointIfNeeded(fn () => $this->create(array_merge($attributes, $values)));
584+
} catch (UniqueConstraintViolationException $exception) {
585+
return $this->useWritePdo()->where($attributes)->first();
586+
}
572587
}
573588

574589
/**
@@ -1694,6 +1709,21 @@ public function withCasts($casts)
16941709
return $this;
16951710
}
16961711

1712+
/**
1713+
* Execute the given Closure within a transaction savepoint if needed.
1714+
*
1715+
* @template TModelValue
1716+
*
1717+
* @param \Closure(): TModelValue $scope
1718+
* @return TModelValue
1719+
*/
1720+
public function withSavepointIfNeeded(Closure $scope): mixed
1721+
{
1722+
return $this->getQuery()->getConnection()->transactionLevel() > 0
1723+
? $this->getQuery()->getConnection()->transaction($scope)
1724+
: $scope();
1725+
}
1726+
16971727
/**
16981728
* Get the underlying query builder instance.
16991729
*

src/Illuminate/Database/Eloquent/Factories/Factory.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,15 @@ public function createOneQuietly($attributes = [])
235235
/**
236236
* Create a collection of models and persist them to the database.
237237
*
238-
* @param int|iterable<int, array<string, mixed>> $records
238+
* @param int|null|iterable<int, array<string, mixed>> $records
239239
* @return \Illuminate\Database\Eloquent\Collection<int, \Illuminate\Database\Eloquent\Model|TModel>
240240
*/
241-
public function createMany(int|iterable $records)
241+
public function createMany(int|iterable|null $records = null)
242242
{
243+
if (is_null($records)) {
244+
$records = $this->count ?? 1;
245+
}
246+
243247
if (is_numeric($records)) {
244248
$records = array_fill(0, $records, []);
245249
}
@@ -254,10 +258,10 @@ public function createMany(int|iterable $records)
254258
/**
255259
* Create a collection of models and persist them to the database without dispatching any model events.
256260
*
257-
* @param int|iterable<int, array<string, mixed>> $records
261+
* @param int|null|iterable<int, array<string, mixed>> $records
258262
* @return \Illuminate\Database\Eloquent\Collection<int, \Illuminate\Database\Eloquent\Model|TModel>
259263
*/
260-
public function createManyQuietly(int|iterable $records)
264+
public function createManyQuietly(int|iterable|null $records = null)
261265
{
262266
return Model::withoutEvents(function () use ($records) {
263267
return $this->createMany($records);

src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Illuminate\Database\Eloquent\Relations\Concerns\AsPivot;
1212
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithDictionary;
1313
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable;
14+
use Illuminate\Database\UniqueConstraintViolationException;
1415
use Illuminate\Support\Str;
1516
use InvalidArgumentException;
1617

@@ -609,7 +610,7 @@ public function firstOrNew(array $attributes = [], array $values = [])
609610
}
610611

611612
/**
612-
* Get the first related record matching the attributes or create it.
613+
* Get the first record matching the attributes. If the record is not found, create it.
613614
*
614615
* @param array $attributes
615616
* @param array $values
@@ -621,15 +622,45 @@ public function firstOrCreate(array $attributes = [], array $values = [], array
621622
{
622623
if (is_null($instance = (clone $this)->where($attributes)->first())) {
623624
if (is_null($instance = $this->related->where($attributes)->first())) {
624-
$instance = $this->create(array_merge($attributes, $values), $joining, $touch);
625+
$instance = $this->createOrFirst($attributes, $values, $joining, $touch);
625626
} else {
626-
$this->attach($instance, $joining, $touch);
627+
try {
628+
$this->getQuery()->withSavepointIfNeeded(fn () => $this->attach($instance, $joining, $touch));
629+
} catch (UniqueConstraintViolationException $exception) {
630+
// Nothing to do, the model was already attached...
631+
}
627632
}
628633
}
629634

630635
return $instance;
631636
}
632637

638+
/**
639+
* Attempt to create the record. If a unique constraint violation occurs, attempt to find the matching record.
640+
*
641+
* @param array $attributes
642+
* @param array $values
643+
* @param array $joining
644+
* @param bool $touch
645+
* @return \Illuminate\Database\Eloquent\Model
646+
*/
647+
public function createOrFirst(array $attributes = [], array $values = [], array $joining = [], $touch = true)
648+
{
649+
try {
650+
return $this->getQuery()->withSavePointIfNeeded(fn () => $this->create(array_merge($attributes, $values), $joining, $touch));
651+
} catch (UniqueConstraintViolationException $exception) {
652+
// ...
653+
}
654+
655+
try {
656+
return tap($this->related->where($attributes)->first(), function ($instance) use ($joining, $touch) {
657+
$this->getQuery()->withSavepointIfNeeded(fn () => $this->attach($instance, $joining, $touch));
658+
});
659+
} catch (UniqueConstraintViolationException $exception) {
660+
return (clone $this)->useWritePdo()->where($attributes)->first();
661+
}
662+
}
663+
633664
/**
634665
* Create or update a related record matching the attributes, and fill it with values.
635666
*

src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Database\Eloquent\Collection;
77
use Illuminate\Database\Eloquent\Model;
88
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithDictionary;
9+
use Illuminate\Database\UniqueConstraintViolationException;
910

1011
abstract class HasOneOrMany extends Relation
1112
{
@@ -226,7 +227,7 @@ public function firstOrNew(array $attributes = [], array $values = [])
226227
}
227228

228229
/**
229-
* Get the first related record matching the attributes or create it.
230+
* Get the first record matching the attributes. If the record is not found, create it.
230231
*
231232
* @param array $attributes
232233
* @param array $values
@@ -235,12 +236,28 @@ public function firstOrNew(array $attributes = [], array $values = [])
235236
public function firstOrCreate(array $attributes = [], array $values = [])
236237
{
237238
if (is_null($instance = $this->where($attributes)->first())) {
238-
$instance = $this->create(array_merge($attributes, $values));
239+
$instance = $this->createOrFirst($attributes, $values);
239240
}
240241

241242
return $instance;
242243
}
243244

245+
/**
246+
* Attempt to create the record. If a unique constraint violation occurs, attempt to find the matching record.
247+
*
248+
* @param array $attributes
249+
* @param array $values
250+
* @return \Illuminate\Database\Eloquent\Model
251+
*/
252+
public function createOrFirst(array $attributes = [], array $values = [])
253+
{
254+
try {
255+
return $this->getQuery()->withSavepointIfNeeded(fn () => $this->create(array_merge($attributes, $values)));
256+
} catch (UniqueConstraintViolationException $exception) {
257+
return $this->useWritePdo()->where($attributes)->first();
258+
}
259+
}
260+
244261
/**
245262
* Create or update a related record matching the attributes, and fill it with values.
246263
*

0 commit comments

Comments
 (0)