Skip to content

Commit 50e2594

Browse files
committed
preparing beta & better testing & bulk setting creation
1 parent b888b53 commit 50e2594

File tree

13 files changed

+434
-17
lines changed

13 files changed

+434
-17
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Laravel
1+
name: Tests
22

33
on:
44
push:

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ php artisan migrate
4747
> > with `Carbon/Carbon`
4848
4949
### Create a Preference
50-
50+
#### single mode
5151
```php
5252
public function up(): void
5353
{
@@ -73,6 +73,30 @@ php artisan migrate
7373
->delete();
7474
}
7575
```
76+
#### Bulk mode
77+
78+
```php
79+
80+
$preferences = [
81+
['name' => 'language', 'cast' => Cast::STRING, 'default_value' => 'en', 'rule'=> new InRule("en", "it", "de"), 'group' => 'general'],
82+
['name' => 'theme', 'cast' => Cast::STRING, 'default_value' => 'light'],
83+
['name' => 'preferred_browser'],
84+
['name' => 'other_short_pref'],
85+
];
86+
87+
public function up(): void
88+
{
89+
PreferenceBuilder::initBulk($this->preferences);
90+
}
91+
92+
/**
93+
* Reverse the migrations.
94+
*/
95+
public function down(): void
96+
{
97+
PreferenceBuilder::deleteBulk($this->$preferences);
98+
}
99+
```
76100

77101
### Working with preferences
78102

@@ -181,6 +205,10 @@ class MyRule extends DataRule
181205
->withRule(new MyRule("Europe","Asia"))
182206
```
183207

208+
## Test
209+
210+
`composer test ./tests`
211+
184212
## Security Vulnerabilities
185213

186214
Please review [our security policy](SECURITY.md) on how to report security vulnerabilities.

composer.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
"Matteoc99\\LaravelPreference\\Tests\\": "tests/"
2020
}
2121
},
22+
"scripts" : {
23+
"analyse" : "vendor/bin/phpstan analyse",
24+
"test" : "vendor/bin/phpunit"
25+
},
2226
"minimum-stability": "dev",
2327
"prefer-stable": true,
2428
"require": {
2529
"php": "^8.1",
26-
"laravel/framework": "^10"
30+
"laravel/framework": "^10",
31+
"ext-pdo": "*"
2732
},
2833
"require-dev": {
2934
"orchestra/testbench": "^8.0",

src/Enums/Cast.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ enum Cast: string implements CastableEnum
1616
case BOOL = 'bool';
1717
case ARRAY = 'array';
1818
case DATE = 'date';
19+
case TIME = 'time';
1920
case DATETIME = 'datetime';
2021
case TIMESTAMP = 'timestamp';
2122

@@ -28,6 +29,7 @@ public function validation(): Rule|string
2829
self::BOOL => 'boolean',
2930
self::ARRAY => 'array',
3031
self::DATE, self::DATETIME => 'date',
32+
self::TIME => 'date_format:H:i',
3133
self::TIMESTAMP => 'date_format:U',
3234
};
3335
}
@@ -41,6 +43,7 @@ public function castFromString(string $value): mixed
4143
self::BOOL => !empty($value),
4244
self::ARRAY => json_encode($value, 1),
4345
self::DATE, self::DATETIME => new Carbon($value),
46+
self::TIME => Carbon::now()->setTimeFromTimeString($value),
4447
self::TIMESTAMP => Carbon::createFromTimestamp($value),
4548
};
4649
}
@@ -55,6 +58,7 @@ public function castToString(mixed $value): string
5558
self::DATE => $value->toDateString(),
5659
self::DATETIME => $value->toDateTimeString(),
5760
self::TIMESTAMP => $value->timestamp,
61+
self::TIME => $value->toTimeString(),
5862
};
5963
}
6064

src/Factory/PreferenceBuilder.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Matteoc99\LaravelPreference\Factory;
44

5+
use Illuminate\Database\Eloquent\Builder;
56
use Matteoc99\LaravelPreference\Contracts\CastableEnum;
67
use Matteoc99\LaravelPreference\Contracts\HasValidation;
78
use Matteoc99\LaravelPreference\Enums\Cast;
@@ -18,7 +19,7 @@ private function __construct()
1819

1920
public static function init(string $name, CastableEnum $cast = Cast::STRING): static
2021
{
21-
$builder = new static();
22+
$builder = new PreferenceBuilder();
2223
return $builder->withName($name)->withCast($cast)->withGroup('general');
2324
}
2425

@@ -80,4 +81,65 @@ public function delete(): int
8081

8182
return $query->delete();
8283
}
84+
85+
public static function initBulk(array $preferences)
86+
{
87+
if (empty($preferences)) {
88+
throw new \InvalidArgumentException("no preferences provided");
89+
}
90+
91+
foreach ($preferences as $key => &$preferenceData) {
92+
if (empty($preferenceData['name'])) {
93+
throw new \InvalidArgumentException(
94+
sprintf("index: #%s name is required", $key)
95+
);
96+
}
97+
98+
if (!($preferenceData['cast'] instanceof CastableEnum)) {
99+
throw new \InvalidArgumentException(
100+
sprintf("index: #%s cast is required and needs to implement 'CastableEnum'", $key)
101+
);
102+
}
103+
104+
// Ensure Defaults
105+
$preferenceData = array_merge([
106+
'group' => 'general',
107+
'default_value' => null,
108+
'description' => '',
109+
'rule' => null,
110+
], $preferenceData);
111+
112+
if ($preferenceData['default_value'] && $preferenceData['rule'] && !$preferenceData['rule']->passes('', $preferenceData['default_value'])) {
113+
throw new \InvalidArgumentException(
114+
sprintf("index: #%s default_value fails the validation rule", $key)
115+
);
116+
}
117+
118+
}
119+
120+
Preference::upsert($preferences, ['name', 'group']);
121+
}
122+
123+
public static function deleteBulk(array $preferences): int
124+
{
125+
if (empty($preferences)) {
126+
throw new \InvalidArgumentException("no preferences provided");
127+
}
128+
$query = Preference::query();
129+
130+
foreach ($preferences as $key => $preferenceData) {
131+
if (empty($preferenceData['name'])) {
132+
throw new \InvalidArgumentException(
133+
sprintf("index: #%s name is required", $key)
134+
);
135+
}
136+
$query->orWhere(function (Builder $query) use ($preferenceData) {
137+
$query->where('name', $preferenceData['name']);
138+
$query->where('group', $preferenceData['group'] ?? 'general');
139+
});
140+
}
141+
142+
return $query->delete();
143+
}
144+
83145
}

src/Models/BaseModel.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22

33
namespace Matteoc99\LaravelPreference\Models;
44

5-
use Carbon\Language;
65
use Illuminate\Database\Eloquent\Model;
76
use Illuminate\Support\Arr;
8-
use Illuminate\Support\Str;
9-
use Illuminate\Validation\Rules\Enum;
10-
use Matteoc99\LaravelPreference\Factory\PreferenceBuilder;
117

128
class BaseModel extends Model
139
{

src/Models/Preference.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Preference extends BaseModel
4848

4949
public function getValidationRules(): array
5050
{
51-
return array_merge(explode(',', $this->cast->validation()), [$this?->rule]);
51+
return array_merge(explode('|', $this->cast->validation()), [$this?->rule]);
5252
}
5353

5454
}

src/Rules/DataRule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
abstract class DataRule implements HasValidation
88
{
99

10-
private mixed $data;
10+
private array $data;
1111

1212
public function __construct(...$data)
1313
{
1414
$this->setData($data);
1515
}
1616

17-
public function getData(): mixed
17+
public function getData(): array
1818
{
1919
return $this->data;
2020
}

src/Traits/HasPreferences.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111

1212
trait HasPreferences
1313
{
14-
/**
15-
* Define eloquent relationships for clarity.
16-
*/
14+
1715
private function userPreferences(): MorphMany
1816
{
1917
return $this->morphMany(UserPreference::class, 'preferenceable');
@@ -88,10 +86,12 @@ public function setPreference(string $name, mixed $value, string $group = 'gener
8886
*
8987
* @param string $name
9088
* @param string $group
89+
*
90+
* @return int
9191
*/
92-
public function removePreference(string $name, string $group = 'general'): void
92+
public function removePreference(string $name, string $group = 'general'): int
9393
{
94-
$this->userPreferences()->whereHas('preference', function ($query) use ($group, $name) {
94+
return $this->userPreferences()->whereHas('preference', function ($query) use ($group, $name) {
9595
$query->where('group', $group)->where('name', $name);
9696
})->delete();
9797
}

tests/CastTest.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace Matteoc99\LaravelPreference\Tests;
4+
5+
use Illuminate\Support\Facades\Validator;
6+
use Matteoc99\LaravelPreference\Enums\Cast;
7+
8+
class CastTest extends TestCase
9+
{
10+
/**
11+
* @test
12+
*/
13+
public function it_validates_correct_int_values()
14+
{
15+
$cast = Cast::INT;
16+
17+
$this->assertTrue(Validator::make(["value" => 123], ['value' => $cast->validation()])->passes());
18+
$this->assertTrue(Validator::make(["value" => "123"], ['value' => $cast->validation()])->passes());
19+
20+
$this->assertFalse(Validator::make(["value" => "hello"], ['value' => $cast->validation()])->passes());
21+
$this->assertFalse(Validator::make(["value" => 12.34], ['value' => $cast->validation()])->passes());
22+
}
23+
24+
/**
25+
* @test
26+
*/
27+
public function it_validates_correct_float_values()
28+
{
29+
$cast = Cast::FLOAT;
30+
31+
$this->assertTrue(Validator::make(["value" => 12.34], ['value' => $cast->validation()])->passes());
32+
$this->assertTrue(Validator::make(["value" => "12.34"], ['value' => $cast->validation()])->passes());
33+
34+
$this->assertFalse(Validator::make(["value" => "hello"], ['value' => $cast->validation()])->passes());
35+
}
36+
37+
/**
38+
* @test
39+
*/
40+
public function it_validates_correct_string_values()
41+
{
42+
$cast = Cast::STRING;
43+
44+
$this->assertTrue(Validator::make(["value" => 'hello world'], ['value' => $cast->validation()])->passes());
45+
$this->assertTrue(Validator::make(["value" => '12345'], ['value' => $cast->validation()])->passes());
46+
$this->assertTrue(Validator::make(["value" => ''], ['value' => $cast->validation()])->passes());
47+
48+
$this->assertFalse(Validator::make(["value" => 123], ['value' => $cast->validation()])->passes());
49+
$this->assertFalse(Validator::make(["value" => true], ['value' => $cast->validation()])->passes());
50+
}
51+
52+
/**
53+
* @test
54+
*/
55+
public function it_validates_correct_bool_values()
56+
{
57+
$cast = Cast::BOOL;
58+
59+
$this->assertTrue(Validator::make(["value" => true], ['value' => $cast->validation()])->passes());
60+
$this->assertTrue(Validator::make(["value" => false], ['value' => $cast->validation()])->passes());
61+
$this->assertTrue(Validator::make(["value" => 0], ['value' => $cast->validation()])->passes());
62+
63+
$this->assertFalse(Validator::make(["value" => 'hello'], ['value' => $cast->validation()])->passes());
64+
$this->assertFalse(Validator::make(["value" => 123], ['value' => $cast->validation()])->passes());
65+
}
66+
67+
/**
68+
* @test
69+
*/
70+
public function it_validates_correct_array_values()
71+
{
72+
$cast = Cast::ARRAY;
73+
74+
$this->assertTrue(Validator::make(["value" => [1, 2, 3]], ['value' => $cast->validation()])->passes());
75+
$this->assertTrue(Validator::make(["value" => ['key' => 'value']], ['value' => $cast->validation()])->passes());
76+
$this->assertTrue(Validator::make(["value" => []], ['value' => $cast->validation()])->passes());
77+
78+
$this->assertFalse(Validator::make(["value" => 123], ['value' => $cast->validation()])->passes());
79+
$this->assertFalse(Validator::make(["value" => 'hello'], ['value' => $cast->validation()])->passes());
80+
}
81+
82+
/**
83+
* @test
84+
*/
85+
public function it_validates_correct_date_values()
86+
{
87+
$cast = Cast::DATE;
88+
89+
$this->assertTrue(Validator::make(["value" => '2023-03-20'], ['value' => $cast->validation()])->passes());
90+
91+
// Add tests with other valid date formats you might support
92+
93+
$this->assertFalse(Validator::make(["value" => 'hello'], ['value' => $cast->validation()])->passes());
94+
$this->assertFalse(Validator::make(["value" => '12:30'], ['value' => $cast->validation()])->passes());
95+
}
96+
97+
/**
98+
* @test
99+
*/
100+
public function it_validates_correct_datetime_values()
101+
{
102+
$cast = Cast::DATETIME;
103+
104+
$this->assertTrue(Validator::make(["value" => '2023-03-20 13:45:00'], ['value' => $cast->validation()])->passes());
105+
106+
// Add tests with other valid datetime formats you might support
107+
108+
$this->assertFalse(Validator::make(["value" => 'hello'], ['value' => $cast->validation()])->passes());
109+
$this->assertFalse(Validator::make(["value" => '12:30'], ['value' => $cast->validation()])->passes());
110+
}
111+
112+
/**
113+
* @test
114+
*/
115+
public function it_validates_correct_timestamp_values()
116+
{
117+
$cast = Cast::TIMESTAMP;
118+
119+
$this->assertTrue(Validator::make(["value" => time()], ['value' => $cast->validation()])->passes());
120+
$this->assertTrue(Validator::make(["value" => 1679190300], ['value' => $cast->validation()])->passes());
121+
122+
$this->assertFalse(Validator::make(["value" => 'hello'], ['value' => $cast->validation()])->passes());
123+
$this->assertFalse(Validator::make(["value" => '2023-03-20 13:45:00'], ['value' => $cast->validation()])->passes());
124+
}
125+
126+
}

0 commit comments

Comments
 (0)