Skip to content

Commit a8d831a

Browse files
authoredJun 5, 2022
Merge pull request #95 from siamak2/2.x
[2.0] laravel meta vesion 2
2 parents 15961bf + 4c385d1 commit a8d831a

13 files changed

+1438
-606
lines changed
 

‎.editorconfig

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_size = 4
6+
indent_style = space
7+
insert_final_newline = false
8+
tab_width = 4
9+
trim_trailing_whitespace = false
10+
ij_continuation_indent_size = 8
11+
ij_formatter_off_tag = @formatter:off
12+
ij_formatter_on_tag = @formatter:on
13+
ij_formatter_tags_enabled = false
14+
ij_smart_tabs = false
15+
ij_visual_guides = none
16+
ij_wrap_on_typing = false
17+
18+
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
19+
indent_style = tab
20+
ij_continuation_indent_size = 4
21+
ij_php_align_assignments = false
22+
ij_php_align_class_constants = false
23+
ij_php_align_group_field_declarations = false
24+
ij_php_align_inline_comments = false
25+
ij_php_align_key_value_pairs = false
26+
ij_php_align_match_arm_bodies = false
27+
ij_php_align_multiline_array_initializer_expression = false
28+
ij_php_align_multiline_binary_operation = false
29+
ij_php_align_multiline_chained_methods = false
30+
ij_php_align_multiline_extends_list = false
31+
ij_php_align_multiline_for = true
32+
ij_php_align_multiline_parameters = true
33+
ij_php_align_multiline_parameters_in_calls = false
34+
ij_php_align_multiline_ternary_operation = false
35+
ij_php_align_named_arguments = false
36+
ij_php_align_phpdoc_comments = false
37+
ij_php_align_phpdoc_param_names = false
38+
ij_php_anonymous_brace_style = end_of_line
39+
ij_php_api_weight = 28
40+
ij_php_array_initializer_new_line_after_left_brace = false
41+
ij_php_array_initializer_right_brace_on_new_line = false
42+
ij_php_array_initializer_wrap = off
43+
ij_php_assignment_wrap = off
44+
ij_php_attributes_wrap = off
45+
ij_php_author_weight = 28
46+
ij_php_binary_operation_sign_on_next_line = false
47+
ij_php_binary_operation_wrap = off
48+
ij_php_blank_lines_after_class_header = 0
49+
ij_php_blank_lines_after_function = 1
50+
ij_php_blank_lines_after_imports = 1
51+
ij_php_blank_lines_after_opening_tag = 0
52+
ij_php_blank_lines_after_package = 1
53+
ij_php_blank_lines_around_class = 1
54+
ij_php_blank_lines_around_constants = 0
55+
ij_php_blank_lines_around_field = 0
56+
ij_php_blank_lines_around_method = 1
57+
ij_php_blank_lines_before_class_end = 0
58+
ij_php_blank_lines_before_imports = 1
59+
ij_php_blank_lines_before_method_body = 0
60+
ij_php_blank_lines_before_package = 1
61+
ij_php_blank_lines_before_return_statement = 0
62+
ij_php_blank_lines_between_imports = 0
63+
ij_php_block_brace_style = end_of_line
64+
ij_php_call_parameters_new_line_after_left_paren = false
65+
ij_php_call_parameters_right_paren_on_new_line = false
66+
ij_php_call_parameters_wrap = off
67+
ij_php_catch_on_new_line = false
68+
ij_php_category_weight = 28
69+
ij_php_class_brace_style = next_line
70+
ij_php_comma_after_last_array_element = true
71+
ij_php_concat_spaces = true
72+
ij_php_copyright_weight = 28
73+
ij_php_deprecated_weight = 28
74+
ij_php_do_while_brace_force = never
75+
ij_php_else_if_style = as_is
76+
ij_php_else_on_new_line = true
77+
ij_php_example_weight = 28
78+
ij_php_extends_keyword_wrap = off
79+
ij_php_extends_list_wrap = off
80+
ij_php_fields_default_visibility = protected
81+
ij_php_filesource_weight = 28
82+
ij_php_finally_on_new_line = false
83+
ij_php_for_brace_force = never
84+
ij_php_for_statement_new_line_after_left_paren = false
85+
ij_php_for_statement_right_paren_on_new_line = false
86+
ij_php_for_statement_wrap = off
87+
ij_php_force_empty_methods_in_one_line = false
88+
ij_php_force_short_declaration_array_style = false
89+
ij_php_getters_setters_naming_style = camel_case
90+
ij_php_getters_setters_order_style = getters_first
91+
ij_php_global_weight = 28
92+
ij_php_group_use_wrap = on_every_item
93+
ij_php_if_brace_force = never
94+
ij_php_if_lparen_on_next_line = false
95+
ij_php_if_rparen_on_next_line = false
96+
ij_php_ignore_weight = 28
97+
ij_php_import_sorting = by_length
98+
ij_php_indent_break_from_case = true
99+
ij_php_indent_case_from_switch = true
100+
ij_php_indent_code_in_php_tags = false
101+
ij_php_internal_weight = 28
102+
ij_php_keep_blank_lines_after_lbrace = 2
103+
ij_php_keep_blank_lines_before_right_brace = 2
104+
ij_php_keep_blank_lines_in_code = 2
105+
ij_php_keep_blank_lines_in_declarations = 2
106+
ij_php_keep_control_statement_in_one_line = true
107+
ij_php_keep_first_column_comment = false
108+
ij_php_keep_indents_on_empty_lines = true
109+
ij_php_keep_line_breaks = true
110+
ij_php_keep_rparen_and_lbrace_on_one_line = false
111+
ij_php_keep_simple_classes_in_one_line = false
112+
ij_php_keep_simple_methods_in_one_line = false
113+
ij_php_lambda_brace_style = end_of_line
114+
ij_php_license_weight = 28
115+
ij_php_line_comment_add_space = false
116+
ij_php_line_comment_at_first_column = false
117+
ij_php_link_weight = 28
118+
ij_php_lower_case_boolean_const = false
119+
ij_php_lower_case_keywords = true
120+
ij_php_lower_case_null_const = false
121+
ij_php_method_brace_style = end_of_line
122+
ij_php_method_call_chain_wrap = off
123+
ij_php_method_parameters_new_line_after_left_paren = false
124+
ij_php_method_parameters_right_paren_on_new_line = false
125+
ij_php_method_parameters_wrap = off
126+
ij_php_method_weight = 28
127+
ij_php_modifier_list_wrap = false
128+
ij_php_multiline_chained_calls_semicolon_on_new_line = false
129+
ij_php_namespace_brace_style = 1
130+
ij_php_new_line_after_php_opening_tag = false
131+
ij_php_null_type_position = in_the_end
132+
ij_php_package_weight = 28
133+
ij_php_param_weight = 0
134+
ij_php_parameters_attributes_wrap = off
135+
ij_php_parentheses_expression_new_line_after_left_paren = false
136+
ij_php_parentheses_expression_right_paren_on_new_line = false
137+
ij_php_phpdoc_blank_line_before_tags = false
138+
ij_php_phpdoc_blank_lines_around_parameters = false
139+
ij_php_phpdoc_keep_blank_lines = true
140+
ij_php_phpdoc_param_spaces_between_name_and_description = 1
141+
ij_php_phpdoc_param_spaces_between_tag_and_type = 1
142+
ij_php_phpdoc_param_spaces_between_type_and_name = 1
143+
ij_php_phpdoc_use_fqcn = false
144+
ij_php_phpdoc_wrap_long_lines = false
145+
ij_php_place_assignment_sign_on_next_line = false
146+
ij_php_place_parens_for_constructor = 0
147+
ij_php_property_read_weight = 28
148+
ij_php_property_weight = 28
149+
ij_php_property_write_weight = 28
150+
ij_php_return_type_on_new_line = false
151+
ij_php_return_weight = 1
152+
ij_php_see_weight = 28
153+
ij_php_since_weight = 28
154+
ij_php_sort_phpdoc_elements = true
155+
ij_php_space_after_colon = true
156+
ij_php_space_after_colon_in_enum_backed_type = true
157+
ij_php_space_after_colon_in_named_argument = true
158+
ij_php_space_after_colon_in_return_type = true
159+
ij_php_space_after_comma = true
160+
ij_php_space_after_for_semicolon = true
161+
ij_php_space_after_quest = true
162+
ij_php_space_after_type_cast = false
163+
ij_php_space_after_unary_not = true
164+
ij_php_space_before_array_initializer_left_brace = false
165+
ij_php_space_before_catch_keyword = true
166+
ij_php_space_before_catch_left_brace = true
167+
ij_php_space_before_catch_parentheses = true
168+
ij_php_space_before_class_left_brace = true
169+
ij_php_space_before_closure_left_parenthesis = true
170+
ij_php_space_before_colon = true
171+
ij_php_space_before_colon_in_enum_backed_type = false
172+
ij_php_space_before_colon_in_named_argument = false
173+
ij_php_space_before_colon_in_return_type = false
174+
ij_php_space_before_comma = false
175+
ij_php_space_before_do_left_brace = true
176+
ij_php_space_before_else_keyword = true
177+
ij_php_space_before_else_left_brace = true
178+
ij_php_space_before_finally_keyword = true
179+
ij_php_space_before_finally_left_brace = true
180+
ij_php_space_before_for_left_brace = true
181+
ij_php_space_before_for_parentheses = true
182+
ij_php_space_before_for_semicolon = false
183+
ij_php_space_before_if_left_brace = true
184+
ij_php_space_before_if_parentheses = true
185+
ij_php_space_before_method_call_parentheses = false
186+
ij_php_space_before_method_left_brace = true
187+
ij_php_space_before_method_parentheses = false
188+
ij_php_space_before_quest = true
189+
ij_php_space_before_short_closure_left_parenthesis = false
190+
ij_php_space_before_switch_left_brace = true
191+
ij_php_space_before_switch_parentheses = true
192+
ij_php_space_before_try_left_brace = true
193+
ij_php_space_before_unary_not = false
194+
ij_php_space_before_while_keyword = true
195+
ij_php_space_before_while_left_brace = true
196+
ij_php_space_before_while_parentheses = true
197+
ij_php_space_between_ternary_quest_and_colon = false
198+
ij_php_spaces_around_additive_operators = true
199+
ij_php_spaces_around_arrow = false
200+
ij_php_spaces_around_assignment_in_declare = false
201+
ij_php_spaces_around_assignment_operators = true
202+
ij_php_spaces_around_bitwise_operators = true
203+
ij_php_spaces_around_equality_operators = true
204+
ij_php_spaces_around_logical_operators = true
205+
ij_php_spaces_around_multiplicative_operators = true
206+
ij_php_spaces_around_null_coalesce_operator = true
207+
ij_php_spaces_around_pipe_in_union_type = false
208+
ij_php_spaces_around_relational_operators = true
209+
ij_php_spaces_around_shift_operators = true
210+
ij_php_spaces_around_unary_operator = false
211+
ij_php_spaces_around_var_within_brackets = false
212+
ij_php_spaces_within_array_initializer_braces = false
213+
ij_php_spaces_within_brackets = false
214+
ij_php_spaces_within_catch_parentheses = false
215+
ij_php_spaces_within_for_parentheses = false
216+
ij_php_spaces_within_if_parentheses = true
217+
ij_php_spaces_within_method_call_parentheses = true
218+
ij_php_spaces_within_method_parentheses = false
219+
ij_php_spaces_within_parentheses = false
220+
ij_php_spaces_within_short_echo_tags = true
221+
ij_php_spaces_within_switch_parentheses = false
222+
ij_php_spaces_within_while_parentheses = false
223+
ij_php_special_else_if_treatment = false
224+
ij_php_subpackage_weight = 28
225+
ij_php_ternary_operation_signs_on_next_line = false
226+
ij_php_ternary_operation_wrap = off
227+
ij_php_throws_weight = 2
228+
ij_php_todo_weight = 28
229+
ij_php_unknown_tag_weight = 28
230+
ij_php_upper_case_boolean_const = false
231+
ij_php_upper_case_null_const = false
232+
ij_php_uses_weight = 28
233+
ij_php_var_weight = 28
234+
ij_php_variable_naming_style = camel_case
235+
ij_php_version_weight = 28
236+
ij_php_while_brace_force = never
237+
ij_php_while_on_new_line = false

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ Thumbs.db
2121
*.dump
2222
*.mmdb
2323
*.history
24+
/vendor/
25+
/.phpunit.result.cache

‎CHANGELOG.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Release Notes
2+
3+
## v2.0.0
4+
5+
### Added
6+
7+
* Added `setAttribute` method.
8+
* Added `hasMeta` method.
9+
* Added `hasDefaultMetaValue` method.
10+
* Added ability to disable fluent meta access by setting `$disableFluentMeta` to `true`.
11+
12+
### Changed
13+
14+
* Removed laravel 7 and bellow support.
15+
* Removed `__get` method.
16+
* Removed `__set` method.
17+
* Removed `__isset` method.
18+
* Removed legacy getter.
19+
* Removed `whereMeta` method in favor of `scopeWhereMeta`.
20+
* Renamed `getMetaDefaultValue` method to `getDefaultMetaValue`.
21+
* You can now set meta names starting with `meta`.
22+
* Changed `saveMeta` method's visibility to public.
23+
* Changed `getMetaData` method's visibility to public.
24+
* Fluent setter will now check for any cast or mutator.
25+
* Passing an array to `getMeta()` will now return a Collection with all the requested metas, even if they don't exist. non-existent metas value would be based on second parameter or `null` if nothing is provided.
26+
27+
### Fixed
28+
29+
* Fixed `getMeta` method's second parameter.
30+
* Fixed duplicate queries executed when retrieving the models stored as meta.
31+
* Fixed fluent getter treating relations as meta when the result is null.

‎IdeHelper.php

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
/**
4+
* this class is used to suppers some IDE warnings
5+
*/
6+
7+
class IdeHelper extends \Illuminate\Database\Eloquent\Model
8+
{
9+
use \Kodeine\Metable\Metable;
10+
}

‎README.md

+129-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Fluent Meta Data for Eloquent Models
2+
23
[![Laravel](https://img.shields.io/badge/Laravel-~8.0-green.svg?style=flat-square)](http://laravel.com)
34
[![Source](http://img.shields.io/badge/source-kodeine/laravel--meta-blue.svg?style=flat-square)](https://github.com/kodeine/laravel-meta/)
45
[![Build Status](http://img.shields.io/travis/kodeine/laravel--meta/master.svg?style=flat-square)](https://travis-ci.org/kodeine/laravel-meta)
@@ -7,19 +8,84 @@
78
Metable Trait adds the ability to access meta data as if it is a property on your model.
89
Metable is Fluent, just like using an eloquent model attribute you can set or unset metas. Follow along the documentation to find out more.
910

11+
## Changelog
12+
13+
visit [CHANGELOG.md](CHANGELOG.md)
14+
1015
## Installation
1116

1217
#### Composer
1318

14-
Add this to your composer.json file, in the require object:
19+
Laravel can be installed on laravel `8.x` or higher.
20+
21+
Run:
22+
23+
```
24+
composer require kodeine/laravel-meta
25+
```
26+
27+
For laravel 7.x or below visit [this link](https://github.com/kodeine/laravel-meta/tree/master).
1528

16-
```javascript
29+
#### Upgrade guide
30+
31+
Change this line in `composer.json`:
32+
33+
```
1734
"kodeine/laravel-meta": "master"
1835
```
1936

20-
After that, run composer install to install the package.
37+
to:
38+
39+
```
40+
"kodeine/laravel-meta": "^2.0"
41+
```
42+
43+
after that, run `composer update` to upgrade the package.
44+
45+
##### Upgrade notice
46+
47+
Laravel meta 2 has some backward incompatible changes that listed below:
48+
49+
1. Laravel 7 or lower not supported.
50+
2. Removed the following methods: `__get`, `__set`, `__isset`. If you have defined any of these methods, then you probably have something like this in your model:
51+
52+
```php
53+
class User extends Model{
54+
use Metable{
55+
__get as __metaGet
56+
}
57+
```
58+
59+
You need to remove `as` operator of the methods.
60+
3. Removed legacy getter. in older version if you had a method called `getSomething()` then you could access return value of this method using `$model->something`. this is no longer the case, and you have to call `$model->getSomething()`.
61+
4. Added new method `setAttribute` that overrides parent method.
62+
5. Renamed `getMetaDefaultValue` method to `getDefaultMetaValue`.
63+
6. Second parameter of `getMeta` method is now default value when meta is null.
64+
7. Removed `whereMeta` method in favor of `scopeWhereMeta`. example: `User::whereMeta($key,$value)->get();`
65+
8. Removed `getModelKey` method.
2166

2267
#### Migration Table Schema
68+
69+
This is an example migration. you need change parts of it.
70+
71+
In this example we assume you have a model named `Post`.
72+
73+
Meta table name should be your model's table name + `_meta` which in this case, model's table name is pluralized form of the model name. so the table name becomes `posts_meta`.
74+
75+
If you don't want to follow this naming convention and use something else for table name, make sure you add this name to your model's body:
76+
77+
```php
78+
protected $metaTable = 'custom_meta_table';
79+
```
80+
81+
the foreign key name should be your model's name + `_id` = `post_id`
82+
83+
If you used something else for foreign key, make sure you add this to your model's body:
84+
85+
```php
86+
protected $metaKeyName = 'custom_foreign_key';
87+
```
88+
2389
```php
2490
/**
2591
* Run the migrations.
@@ -29,9 +95,9 @@ After that, run composer install to install the package.
2995
public function up()
3096
{
3197
Schema::create('posts_meta', function (Blueprint $table) {
32-
$table->increments('id');
98+
$table->bigIncrements('id');
3399

34-
$table->integer('post_id')->unsigned()->index();
100+
$table->bigInteger('post_id')->unsigned();
35101
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
36102

37103
$table->string('type')->default('null');
@@ -53,8 +119,8 @@ public function down()
53119
Schema::drop('posts_meta');
54120
}
55121
```
56-
## Configuration
57122

123+
## Configuration
58124

59125
#### Model Setup
60126

@@ -70,7 +136,7 @@ class Post extends Eloquent
70136
```
71137

72138
Metable Trait will automatically set the meta table based on your model name.
73-
Default meta table name would be, `model_meta`.
139+
Default meta table name would be, `models_meta` where `models` is pluralized form of the model name.
74140
In case you need to define your own meta table name, you can specify in model:
75141

76142
```php
@@ -82,15 +148,14 @@ class Post extends Eloquent
82148

83149
#### Default Model Attribute values
84150

85-
Additionally, you can set default values by setting an array called `$defaultMetaValues` on the model. Setting default has two side-effects:
86-
87-
1. If a meta attribute does not exist, the default value will be returned instead of `null`.
151+
Additionally, you can set default values by setting an array called `$defaultMetaValues` on the model. Setting default has two side effects:
88152

89-
2. if you attempt to set a meta attribute to the default value, the row in the meta table will be removed, which will cause the default value to be returned, as per rule 1.
153+
1. If a meta attribute does not exist, the default value will be returned instead of `null`.
154+
2. if you attempt to set a meta attribute to the default value, the row in the meta table will be removed, which will cause the default value to be returned, as per rule 1.
90155

91-
This is be the desired and expected functionality for most projects, but be aware that you may need to reimplement default functionality with your own custom accessors and mutators if this functionality does not fit your needs.
156+
This is being the desired and expected functionality for most projects, but be aware that you may need to reimplement default functionality with your own custom accessors and mutators if this functionality does not fit your needs.
92157

93-
This functionality is most suited for meta entries that note exceptions to rules. For example: employees sick out of office (default value: in office), nodes taken down for maintance (default value: node up), etc. This means the table doesn't need to store data on every entry which is in the expected state, only those rows in the exceptional state, and allows the rows to have a default state upon creation without needing to add code to write it.
158+
This functionality is most suited for meta entries that note exceptions to rules. For example: employees sick out of office (default value: in office), nodes taken down for maintenance (default value: node up), etc. This means the table doesn't need to store data on every entry which is in the expected state, only those rows in the exceptional state, and allows the rows to have a default state upon creation without needing to add code to write it.
94159

95160
```
96161
public $defaultMetaValues = [
@@ -99,6 +164,7 @@ This functionality is most suited for meta entries that note exceptions to rules
99164
```
100165

101166
#### Gotcha
167+
102168
When you extend a model and still want to use the same meta table you must override `getMetaKeyName` function.
103169

104170
```
@@ -116,17 +182,15 @@ class Slideshow extends Post
116182
}
117183
```
118184

119-
120-
121185
## Working With Meta
122186

123187
#### Setting Content Meta
124188

125189
To set a meta value on an existing piece of content or create a new data:
126190

127191
> **Fluent way**, You can **set meta flawlessly** as you do on your regular eloquent models.
128-
Metable checks if attribute belongs to model, if not it will
129-
access meta model to append or set a new meta.
192+
> Metable checks if attribute belongs to model, if not it will
193+
> access meta model to append or set a new meta.
130194
131195
```php
132196
$post = Post::find(1);
@@ -169,6 +233,13 @@ $post->save();
169233

170234
> **Note:** If a piece of content already has a meta the existing value will be updated.
171235
236+
You can also save metas with `saveMeta` without saving the model itself:
237+
238+
```php
239+
$post->content = 'some content goes here'; // meta data attribute
240+
$post->saveMeta(); // will save metas to database but won't save the model itself
241+
```
242+
172243
#### Unsetting Content Meta
173244

174245
Similarly, you may unset meta from an existing piece of content:
@@ -214,15 +285,29 @@ To see if a piece of content has a meta:
214285
```php
215286
if (isset($post->content)) {
216287

288+
}
289+
// or
290+
if ($post->hasMeta('content')){
291+
217292
}
218293
```
219294

295+
You may also check if model has multiple metas:
296+
297+
```php
298+
$post->hasMeta(['content','views']); // returns true only if all the metas exist
299+
// or
300+
$post->hasMeta('content|views');
301+
// or
302+
$post->hasMeta('content,views');
303+
```
304+
220305
#### Retrieving Meta
221306

222307
To retrieve a meta value on a piece of content, use the `getMeta` method:
223308

224309
> **Fluent way**, You can access meta data as if it is a property on your model.
225-
Just like you do on your regular eloquent models.
310+
> Just like you do on your regular eloquent models.
226311
227312
```php
228313
$post = Post::find(1);
@@ -242,13 +327,38 @@ Or specify a default value, if not set:
242327
$post = $post->getMeta('content', 'Something');
243328
```
244329

330+
> **Note:** default values set in defaultMetaValues property take precedence over default value passed to this method.
331+
245332
You may also retrieve more than one meta at a time and get an illuminate collection:
246333

247334
```php
248335
// using comma or pipe
249336
$post = $post->getMeta('content|views');
250337
// or an array
251338
$post = $post->getMeta(['content', 'views']);
339+
// specify default values
340+
$post->getMeta(['content', 'views'],['content'=>'something','views'=>0]);
341+
// or specify one default value for all missing metas
342+
$post->getMeta(['content', 'views'],'none');// result if the metas are missing: ['content'=>'none','views'=>'none']
343+
// without specifying default value result will be null
344+
$post->getMeta(['content', 'views']);// result if the metas are missing: ['content'=>null,'views'=>null]
345+
```
346+
347+
#### Disable fluent access
348+
349+
If you don't want to access metas in fluent way, you can disable it by adding following property to your model:
350+
351+
```php
352+
protected $disableFluentMeta = true;
353+
```
354+
355+
By setting that property, this package will no longer handle metas in the following ways:
356+
357+
```php
358+
$post->content='something';// will not set meta. original laravel action will be taken
359+
$post->content;// will not retrieve meta
360+
unset($post->content);// will not unset meta
361+
isset($post->content);// will not check if meta exists
252362
```
253363

254364
#### Retrieving All Metas
@@ -283,7 +393,7 @@ $post = Post::meta()
283393

284394
#### Eager Loading
285395

286-
When you need to retrive multiple results from your model, you can eager load `metas`
396+
When you need to retrieve multiple results from your model, you can eager load `metas`
287397

288398
```php
289399
$post = Post::with(['metas'])->get();

‎bootstrapTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
require 'vendor/autoload.php';

‎composer.json

+44-33
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,46 @@
11
{
2-
"name": "kodeine/laravel-meta",
3-
"description": "Fluent Meta Data for Eloquent Models, as if it is a property on your model.",
4-
"keywords": [
5-
"laravel",
6-
"meta",
7-
"metas",
8-
"meta data",
9-
"data",
10-
"metadata",
11-
"metable",
12-
"model",
13-
"eloquent",
14-
"kodeine"
15-
],
16-
"authors": [
17-
{
18-
"name": "Ahsen M.",
19-
"homepage": "https://github.com/kodeine",
20-
"role": "Developer"
21-
}
22-
],
23-
"require": {
24-
"php": ">=5.4.0",
25-
"illuminate/support": "~5.0|~5.1|^6.0|^7.0|^8.0|^9.0"
26-
},
27-
"autoload": {
28-
"classmap": [],
29-
"psr-0": {
30-
"Kodeine\\Metable\\": "src/"
31-
}
32-
},
33-
"minimum-stability": "stable",
34-
"license": "MIT"
2+
"name": "kodeine/laravel-meta",
3+
"description": "Fluent Meta Data for Eloquent Models, as if it is a property on your model.",
4+
"keywords": [
5+
"laravel",
6+
"meta",
7+
"metas",
8+
"meta data",
9+
"data",
10+
"metadata",
11+
"metable",
12+
"model",
13+
"eloquent",
14+
"kodeine"
15+
],
16+
"authors": [
17+
{
18+
"name": "Ahsen M.",
19+
"homepage": "https://github.com/kodeine",
20+
"role": "Developer"
21+
}
22+
],
23+
"require": {
24+
"php": ">=7.3",
25+
"illuminate/support": "^8.0|^9.0",
26+
"illuminate/database": "^8.0|^9.0",
27+
"illuminate/events": "^8.0|^9.0",
28+
"ext-json": "*"
29+
},
30+
"autoload": {
31+
"classmap": [],
32+
"psr-0": {
33+
"Kodeine\\Metable\\": "src/"
34+
}
35+
},
36+
"autoload-dev": {
37+
"psr-4": {
38+
"Kodeine\\Metable\\Tests\\": "tests/"
39+
}
40+
},
41+
"minimum-stability": "stable",
42+
"license": "MIT",
43+
"require-dev": {
44+
"phpunit/phpunit": "^9.5"
45+
}
3546
}

‎phpunit.xml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<phpunit bootstrap="bootstrapTest.php"
2+
colors="true"
3+
>
4+
<testsuites>
5+
<testsuite name="Unit">
6+
<directory suffix="Test.php">tests</directory>
7+
</testsuite>
8+
</testsuites>
9+
<coverage processUncoveredFiles="true">
10+
<include>
11+
<directory suffix=".php">./src</directory>
12+
</include>
13+
</coverage>
14+
</phpunit>

‎src/Kodeine/Metable/MetaData.php

+111-92
Original file line numberDiff line numberDiff line change
@@ -5,98 +5,117 @@
55
use DateTime;
66
use Illuminate\Database\Eloquent\Model;
77

8+
/**
9+
* @property string $type
10+
*/
811
class MetaData extends Model
912
{
10-
/**
11-
* @var array
12-
*/
13-
protected $fillable = ['key', 'value'];
14-
15-
/**
16-
* @var array
17-
*/
18-
protected $dataTypes = ['boolean', 'integer', 'double', 'float', 'string', 'NULL'];
19-
20-
/**
21-
* Whether or not to delete the Data on save.
22-
*
23-
* @var bool
24-
*/
25-
protected $markForDeletion = false;
26-
27-
/**
28-
* Whether or not to delete the Data on save.
29-
*
30-
* @param bool $bool
31-
*/
32-
public function markForDeletion($bool = true)
33-
{
34-
$this->markForDeletion = $bool;
35-
}
36-
37-
/**
38-
* Check if the model needs to be deleted.
39-
*
40-
* @return bool
41-
*/
42-
public function isMarkedForDeletion()
43-
{
44-
return (bool) $this->markForDeletion;
45-
}
46-
47-
/**
48-
* Set the value and type.
49-
*
50-
* @param $value
51-
*/
52-
public function setValueAttribute($value)
53-
{
54-
$type = gettype($value);
55-
56-
if (is_array($value)) {
57-
$this->type = 'array';
58-
$this->attributes['value'] = json_encode($value);
59-
} elseif ($value instanceof DateTime) {
60-
$this->type = 'datetime';
61-
$this->attributes['value'] = $this->fromDateTime($value);
62-
} elseif ($value instanceof Model) {
63-
$this->type = 'model';
64-
$this->attributes['value'] = get_class($value).(!$value->exists ? '' : '#'.$value->getKey());
65-
} elseif (is_object($value)) {
66-
$this->type = 'object';
67-
$this->attributes['value'] = json_encode($value);
68-
} else {
69-
$this->type = in_array($type, $this->dataTypes) ? $type : 'string';
70-
$this->attributes['value'] = $value;
71-
}
72-
}
73-
74-
public function getValueAttribute($value)
75-
{
76-
$type = $this->type ?: 'null';
77-
78-
switch ($type) {
79-
case 'array':
80-
return json_decode($value, true);
81-
case 'object':
82-
return json_decode($value);
83-
case 'datetime':
84-
return $this->asDateTime($value);
85-
case 'model': {
86-
if (strpos($value, '#') === false) {
87-
return new $value();
88-
}
89-
90-
list($class, $id) = explode('#', $value);
91-
92-
return with(new $class())->findOrFail($id);
93-
}
94-
}
95-
96-
if (in_array($type, $this->dataTypes)) {
97-
settype($value, $type);
98-
}
99-
100-
return $value;
101-
}
13+
/**
14+
* @var array
15+
*/
16+
protected $fillable = ['key', 'value'];
17+
18+
/**
19+
* @var array
20+
*/
21+
protected $dataTypes = ['boolean', 'integer', 'double', 'float', 'string', 'NULL'];
22+
23+
/**
24+
* Whether or not to delete the Data on save.
25+
*
26+
* @var bool
27+
*/
28+
protected $markForDeletion = false;
29+
30+
protected $modelCache = [];
31+
32+
/**
33+
* Whether or not to delete the Data on save.
34+
*
35+
* @param bool $bool
36+
*/
37+
public function markForDeletion(bool $bool = true) {
38+
$this->markForDeletion = $bool;
39+
}
40+
41+
/**
42+
* Check if the model needs to be deleted.
43+
*
44+
* @return bool
45+
*/
46+
public function isMarkedForDeletion(): bool {
47+
return $this->markForDeletion;
48+
}
49+
50+
/**
51+
* Set the value and type.
52+
*
53+
* @param $value
54+
*/
55+
public function setValueAttribute($value) {
56+
$type = gettype( $value );
57+
58+
if ( is_array( $value ) ) {
59+
$this->type = 'array';
60+
$this->attributes['value'] = json_encode( $value );
61+
}
62+
elseif ( $value instanceof DateTime ) {
63+
$this->type = 'datetime';
64+
$this->attributes['value'] = $this->fromDateTime( $value );
65+
}
66+
elseif ( $value instanceof Model ) {
67+
$this->type = 'model';
68+
$class = get_class( $value );
69+
$this->attributes['value'] = $class . (! $value->exists ? '' : '#' . $value->getKey());
70+
// Update the cache
71+
$this->modelCache[$class][$value->getKey()] = $value;
72+
}
73+
elseif ( is_object( $value ) ) {
74+
$this->type = 'object';
75+
$this->attributes['value'] = json_encode( $value );
76+
}
77+
else {
78+
$this->type = in_array( $type, $this->dataTypes ) ? $type : 'string';
79+
$this->attributes['value'] = $value;
80+
}
81+
}
82+
83+
public function getValueAttribute($value) {
84+
$type = $this->type ?: 'null';
85+
86+
switch ($type) {
87+
case 'array':
88+
return json_decode( $value, true );
89+
case 'object':
90+
return json_decode( $value );
91+
case 'datetime':
92+
return $this->asDateTime( $value );
93+
case 'model':
94+
{
95+
if ( strpos( $value, '#' ) === false ) {
96+
return new $value();
97+
}
98+
99+
list( $class, $id ) = explode( '#', $value );
100+
101+
return $this->resolveModelInstance( $class, $id );
102+
}
103+
}
104+
105+
if ( in_array( $type, $this->dataTypes ) ) {
106+
settype( $value, $type );
107+
}
108+
109+
return $value;
110+
}
111+
112+
protected function resolveModelInstance($model, $Key) {
113+
if ( ! isset( $this->modelCache[$model] ) ) {
114+
$this->modelCache[$model] = [];
115+
}
116+
if ( ! isset( $this->modelCache[$model][$Key] ) ) {
117+
$this->modelCache[$model][$Key] = (new $model())->findOrFail( $Key );
118+
}
119+
return $this->modelCache[$model][$Key];
120+
}
102121
}

‎src/Kodeine/Metable/Metable.php

+466-460
Large diffs are not rendered by default.

‎src/Kodeine/Metable/Migrations/create_posts_meta_table.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ class CreatePostsMetaTable extends Migration
1313
public function up()
1414
{
1515
Schema::create('posts_meta', function (Blueprint $table) {
16-
$table->increments('id');
16+
$table->bigIncrements('id');
1717

18-
$table->integer('post_id')->unsigned()->index();
18+
$table->bigInteger('post_id')->unsigned();
1919
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
2020

2121
$table->string('type')->default('null');

‎tests/MetableTest.php

+346
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
<?php /** @noinspection PhpUndefinedMethodInspection */
2+
3+
/** @noinspection PhpUndefinedFieldInspection */
4+
5+
namespace Kodeine\Metable\Tests;
6+
7+
use stdClass;
8+
use DateTime;
9+
use PHPUnit\Framework\TestCase;
10+
use Kodeine\Metable\Tests\Models\User;
11+
use Illuminate\Database\Capsule\Manager as Capsule;
12+
use Illuminate\Database\Eloquent\ModelNotFoundException;
13+
14+
class MetableTest extends TestCase
15+
{
16+
protected function setUp(): void {
17+
$capsule = new Capsule;
18+
$capsule->addConnection( [
19+
'driver' => 'sqlite',
20+
'database' => ':memory:',
21+
'charset' => 'utf8',
22+
'collation' => 'utf8_unicode_ci',
23+
'prefix' => '',
24+
'foreign_key_constraints' => true,
25+
] );
26+
$capsule->setAsGlobal();
27+
$capsule->bootEloquent();
28+
Capsule::schema()->enableForeignKeyConstraints();
29+
Capsule::schema()->create( 'users', function ($table) {
30+
$table->id();
31+
$table->string( 'name' )->default( 'john' );
32+
$table->string( 'email' )->default( 'john@doe.com' );
33+
$table->string( 'password' )->nullable();
34+
$table->integer( 'user_id' )->unsigned()->nullable();
35+
$table->foreign( 'user_id' )->references( 'id' )->on( 'users' );
36+
$table->timestamps();
37+
} );
38+
Capsule::schema()->create( 'users_meta', function ($table) {
39+
$table->id();
40+
$table->integer( 'user_id' )->unsigned();
41+
$table->foreign( 'user_id' )->references( 'id' )->on( 'users' )->onDelete( 'cascade' );
42+
$table->string( 'type' )->default( 'null' );
43+
$table->string( 'key' )->index();
44+
$table->text( 'value' )->nullable();
45+
46+
$table->timestamps();
47+
} );
48+
/*Capsule::schema()->table( 'users_meta', function ($table) {
49+
$table->foreign( 'user_id' )->references( 'id' )->on( 'users' )->onDelete( 'cascade' );
50+
} );*/
51+
}
52+
53+
public function testFluentMeta() {
54+
$user = new User;
55+
56+
$this->assertNull( $user->foo, 'Meta should be null by default' );
57+
58+
$user->foo = 'bar';
59+
60+
/** @noinspection PhpConditionAlreadyCheckedInspection */
61+
$this->assertTrue( isset( $user->foo ), 'Fluent meta should be set before save.' );
62+
$this->assertEquals( 'bar', $user->foo, 'Fluent setter not working before save.' );
63+
$this->assertEquals( 0, Capsule::table( 'users_meta' )->count(), 'Fluent setter should not save to database before save.' );
64+
65+
$this->assertNull( $user->dummy, 'Dummy relation should be null by default' );
66+
$this->dummy = 'dummy';
67+
68+
$user->save();
69+
70+
$this->assertNull( $user->dummy, 'Dummy relation should be null after setting meta named dummy' );
71+
72+
$metaData = Capsule::table( 'users_meta' )->where( $user->getMetaKeyName(), $user->getKey() )->where( 'key', 'foo' );
73+
74+
$this->assertTrue( isset( $user->foo ), 'Fluent meta should be set.' );
75+
$this->assertEquals( 'bar', $user->foo, 'Fluent setter not working.' );
76+
$this->assertEquals( 'bar', is_null( $meta = $metaData->first() ) ? null : $meta->value, 'Fluent setter did not save meta to database.' );
77+
78+
$user->foo = 'baz';
79+
80+
$this->assertEquals( 'baz', $user->foo, 'Fluent setter did not update existing meta before save.' );
81+
$this->assertEquals( 'bar', is_null( $meta = $metaData->first() ) ? null : $meta->value, 'Fluent setter should not update meta in database before save.' );
82+
83+
$user->save();
84+
85+
$this->assertEquals( 'baz', $user->foo, 'Fluent setter did not update existing meta.' );
86+
$this->assertEquals( 'baz', is_null( $meta = $metaData->first() ) ? null : $meta->value, 'Fluent setter did not update meta in database.' );
87+
$this->assertEquals( 1, $metaData->count(), 'Fluent setter created multiple rows for one meta data.' );
88+
89+
unset( $user->foo );
90+
91+
$this->assertNull( $user->foo, 'Unsetter did not work before save.' );
92+
$this->assertEquals( 'baz', is_null( $meta = $metaData->first() ) ? null : $meta->value, 'Fluent unsetter should not remove meta from database before save.' );
93+
$this->assertEquals( 1, $metaData->count(), 'Fluent unsetter should not remove meta from database before save.' );
94+
$this->assertFalse( isset( $user->foo ), 'Fluent meta should not be set before save.' );
95+
96+
$user->save();
97+
98+
$this->assertNull( $user->foo, 'Unsetter did not work.' );
99+
$this->assertNull( $metaData->first(), 'Fluent unsetter did not remove meta from database.' );
100+
$this->assertEquals( 0, $metaData->count(), 'Fluent unsetter did not remove meta from database.' );
101+
$this->assertFalse( isset( $user->foo ), 'Fluent meta should not be set.' );
102+
103+
$user->foo = 'bar';
104+
$user->save();
105+
$user->delete();
106+
107+
$this->assertEquals( 0, $metaData->count(), 'Meta should be deleted from database after deleting user.' );
108+
}
109+
110+
public function testDisableFluentMeta() {
111+
$user = new User;
112+
$user->disableFluentMeta = true;
113+
114+
$user->foo = 'bar';
115+
116+
$this->assertNull( $user->getMeta( 'foo' ), 'Meta should be null.' );
117+
$this->assertFalse( $user->hasMeta( 'foo' ), 'Meta should not be set.' );
118+
119+
$user->setMeta( 'foo', 'baz' );
120+
121+
$this->assertNotEquals( 'baz', $user->foo, 'Fluent getter should not be changed.' );
122+
$this->assertEquals( 'baz', $user->getMeta( 'foo' ), 'meta should be set.' );
123+
124+
unset( $user->foo );
125+
126+
$this->assertEquals( 'baz', $user->getMeta( 'foo' ), 'meta should be set.' );
127+
128+
$user->foo = 'bar';
129+
$user->unsetMeta( 'foo' );
130+
131+
$this->assertNull( $user->getMeta( 'foo' ), 'meta should not be set.' );
132+
$this->assertEquals( 'bar', $user->foo, 'Fluent getter should not be changed.' );
133+
}
134+
135+
public function testScopes() {
136+
$user1 = new User;
137+
$user1->foo = 'bar';
138+
$user1->save();
139+
140+
$user2 = new User;
141+
$user2->foo = 'baz';
142+
$user2->save();
143+
144+
$scope = User::meta()->where( 'users_meta.key', 'foo' )->where( 'users_meta.value', 'baz' );
145+
$user = $scope->first();
146+
$this->assertEquals( $user2->getKey(), $user->getKey(), 'Meta scope found wrong user' );
147+
$this->assertEquals( $user->foo, $user2->foo, 'Meta scope found wrong user' );
148+
$this->assertNotNull( $user->metas, 'Metas relation should not be null' );
149+
150+
$scope = User::whereMeta( 'foo', 'baz' );
151+
$user = $scope->first();
152+
$this->assertEquals( $user2->getKey(), $user->getKey(), 'WhereMeta scope found wrong user' );
153+
$this->assertEquals( $user->foo, $user2->foo, 'WhereMeta scope found wrong user' );
154+
155+
$user1->delete();
156+
$user2->delete();
157+
}
158+
159+
public function testDefaultMetaValues() {
160+
$user = new User;
161+
162+
$this->assertTrue( isset( $user->default_meta_key ), 'Default meta key should be set' );
163+
$this->assertTrue( $user->hasDefaultMetaValue( 'default_meta_key' ), 'Default meta key should be set' );
164+
$this->assertFalse( $user->hasDefaultMetaValue( 'foo' ), 'Non Default meta key should not be set' );
165+
$this->assertEquals( 'default_meta_value', $user->default_meta_key, 'Default meta value should be set' );
166+
$user->default_meta_key = 'foo';
167+
$this->assertEquals( 'foo', $user->default_meta_key, 'Default meta value should be changed' );
168+
169+
$user->save();
170+
$metaData = Capsule::table( 'users_meta' )->where( $user->getMetaKeyName(), $user->getKey() )->where( 'key', 'default_meta_key' );
171+
172+
$this->assertEquals( 'foo', is_null( $meta = $metaData->first() ) ? null : $meta->value, 'Default value should be changed in database.' );
173+
174+
$user->default_meta_key = 'default_meta_value';
175+
$user->save();
176+
177+
$this->assertNull( $metaData->first(), 'Default value should be removed from database.' );
178+
179+
$user->setMeta( 'default_meta_key', 'foo' );
180+
$user->save();
181+
182+
$this->assertEquals( 'foo', is_null( $meta = $metaData->first() ) ? null : $meta->value, 'Default value should be changed in database.' );
183+
184+
$user->setMeta( 'default_meta_key', 'default_meta_value' );
185+
$user->save();
186+
187+
$this->assertNull( $metaData->first(), 'Default value should be removed from database.' );
188+
189+
$user->delete();
190+
}
191+
192+
public function testAccessorAndMutator() {
193+
$user = new User;
194+
195+
$this->assertTrue( isset( $user->accessor ), 'Meta accessor key should be set' );
196+
$this->assertEquals( 'accessed_', $user->accessor, 'Meta accessor value should be set' );
197+
198+
$user->accessor = 'foo';
199+
$this->assertEquals( 'accessed_foo', $user->accessor, 'Meta accessor value should be changed' );
200+
201+
$this->assertFalse( isset( $user->mutator ), 'Meta mutator key should not be set' );
202+
203+
$user->mutator = 'foo';
204+
$this->assertEquals( 'mutated_foo', $user->mutator, 'Meta mutator value should be changed' );
205+
}
206+
207+
public function testMetaMethods() {
208+
$user = new User;
209+
210+
$user->setMeta( 'foo', 'bar' );
211+
$this->assertEquals( 'bar', $user->getMeta( 'foo' ), 'Meta method getMeta did not return correct value' );
212+
213+
$user->setMeta( [
214+
'foo' => 'baz',
215+
'bas' => 'bar',
216+
] );
217+
218+
$user->save();
219+
220+
// re retrieve user to make sure meta is saved
221+
$user = User::with( ['metas'] )->find( $user->getKey() );
222+
223+
$this->assertTrue( $user->relationLoaded( 'metas' ), 'Metas relation should be loaded' );
224+
225+
$this->assertEquals( 'baz', $user->getMeta( 'foo' ), 'Meta method getMeta did not return correct value' );
226+
$this->assertEquals( 'bar', $user->getMeta( 'bas' ), 'Meta method getMeta did not return correct value' );
227+
$this->assertSame( ['foo' => 'baz', 'bas' => 'bar'], $user->getMeta()->toArray(), 'Meta method getMeta did not return correct value' );
228+
$this->assertSame( ['foo' => 'baz', 'bas' => 'bar'], $user->getMeta( ['foo', 'bas'] )->toArray(), 'Meta method getMeta did not return correct value' );
229+
$this->assertSame( ['foo' => 'baz', 'bas' => 'bar'], $user->getMeta( 'foo|bas' )->toArray(), 'Meta method getMeta did not return correct value' );
230+
$this->assertSame( ['foo' => 'baz'], $user->getMeta( ['foo'] )->toArray(), 'Meta method getMeta did not return correct value' );
231+
232+
$this->assertTrue( $user->hasMeta( 'foo' ), 'Meta method hasMeta did not return correct value' );
233+
$this->assertFalse( $user->hasMeta( 'bar' ), 'Meta method hasMeta did not return correct value' );
234+
$this->assertTrue( $user->hasMeta( ['foo', 'bas'] ), 'Meta method hasMeta did not return correct value' );
235+
$this->assertTrue( $user->hasMeta( ['foo|bas'] ), 'Meta method hasMeta did not return correct value' );
236+
$this->assertFalse( $user->hasMeta( ['foo', 'bar'] ), 'Meta method hasMeta did not return correct value' );
237+
238+
$user->unsetMeta( 'foo' );
239+
$this->assertFalse( $user->hasMeta( 'foo' ), 'Meta method hasMeta did not return correct value' );
240+
$this->assertNull( $user->getMeta( 'foo' ), 'Meta method getMeta did not return correct value' );
241+
242+
$user->setMeta( 'foo', 'bar' );
243+
$user->setMeta( 'bar', 'baz' );
244+
$user->unsetMeta( ['foo', 'bas'] );
245+
246+
$this->assertFalse( $user->hasMeta( 'foo' ), 'Meta method hasMeta did not return correct value' );
247+
$this->assertFalse( $user->hasMeta( 'bas' ), 'Meta method hasMeta did not return correct value' );
248+
$this->assertFalse( $user->hasMeta( ['foo', 'bas'] ), 'Meta method hasMeta did not return correct value' );
249+
$this->assertFalse( $user->hasMeta( ['foo|bas'] ), 'Meta method hasMeta did not return correct value' );
250+
$this->assertFalse( $user->hasMeta( ['foo', 'bar'] ), 'Meta method hasMeta did not return correct value' );
251+
$this->assertTrue( $user->hasMeta( ['bar'] ), 'Meta method hasMeta did not return correct value' );
252+
253+
$user->delete();
254+
}
255+
256+
public function testDefaultParameterInGetMeta() {
257+
$user = new User;
258+
259+
$this->assertEquals( 'default_value', $user->getMeta( 'foo', 'default_value' ), 'Default parameter should be returned when meta is null' );
260+
$this->assertSame( ['foo' => 'foo_value', 'bar' => 'bar_value'], $user->getMeta( ['foo', 'bar'], ['foo' => 'foo_value', 'bar' => 'bar_value'] )->toArray(), 'Default parameter should be returned when meta is null' );
261+
$this->assertSame( ['foo' => 'default_value', 'bar' => 'default_value'], $user->getMeta( ['foo', 'bar'], 'default_value' )->toArray(), 'Default parameter should be returned when meta is null' );
262+
263+
$this->assertEquals( 'default_meta_value', $user->getMeta( 'default_meta_key', 'bar' ), 'Default value set in defaultMetaValues property should be returned when meta is null' );
264+
$this->assertSame( ['default_meta_key' => 'default_meta_value', 'foo' => 'bar'], $user->getMeta( ['default_meta_key', 'foo'], ['default_meta_key' => 'bar', 'foo' => 'bar'] )->toArray(), 'Default value set in defaultMetaValues property should be returned when meta is null' );
265+
$this->assertSame( ['default_meta_key' => 'default_meta_value', 'foo' => 'bar'], $user->getMeta( ['default_meta_key', 'foo'], 'bar' )->toArray(), 'Default value set in defaultMetaValues property should be returned when meta is null' );
266+
}
267+
268+
public function testHasColumn() {
269+
$user = new User;
270+
$this->assertTrue( $user->hasColumn( 'name' ), 'User does not have "name" column' );
271+
$this->assertFalse( $user->hasColumn( 'foo' ), 'User should not have "foo" column' );
272+
}
273+
274+
public function testHideMeta() {
275+
$user = new User;
276+
$user->setMeta( 'foo', 'bar' );
277+
$user->hideMeta = false;
278+
279+
$this->assertArrayHasKey( 'meta_data', $user->toArray(), 'Metas should be included in array' );
280+
$this->assertArrayHasKey( 'foo', $user->toArray()['meta_data'], 'Meta should be included in array' );
281+
282+
$user->hideMeta = true;
283+
$this->assertArrayNotHasKey( 'meta_data', $user->toArray(), 'Metas should not be included in array' );
284+
285+
}
286+
287+
public function testMetaDataTypeStoredCorrectly() {
288+
$user = new User;
289+
$user->setMeta( 'string', 'string' );
290+
$user->setMeta( 'integer', 1 );
291+
$user->setMeta( 'double', 1.1 );
292+
$user->setMeta( 'boolean', true );
293+
$user->setMeta( 'array', [1, 2, 3] );
294+
$user->setMeta( 'object', new stdClass );
295+
$user->setMeta( 'null' );
296+
$user->setMeta( 'datetime', new DateTime );
297+
298+
$user2 = new User;
299+
$user2->save();
300+
$user->setMeta( 'model', $user2 );
301+
$user->save();
302+
// reload user
303+
$user = User::find( $user->id );
304+
305+
$this->assertEquals( 'string', $user->getMetaData()->get( 'string' )->type );
306+
$this->assertEquals( 'string', gettype( $user->getMeta( 'string' ) ) );
307+
308+
$this->assertEquals( 'integer', $user->getMetaData()->get( 'integer' )->type );
309+
$this->assertEquals( 'integer', gettype( $user->getMeta( 'integer' ) ) );
310+
311+
$this->assertEquals( 'double', $user->getMetaData()->get( 'double' )->type );
312+
$this->assertEquals( 'double', gettype( $user->getMeta( 'double' ) ) );
313+
314+
$this->assertEquals( 'boolean', $user->getMetaData()->get( 'boolean' )->type );
315+
$this->assertEquals( 'boolean', gettype( $user->getMeta( 'boolean' ) ) );
316+
317+
$this->assertEquals( 'array', $user->getMetaData()->get( 'array' )->type );
318+
$this->assertEquals( 'array', gettype( $user->getMeta( 'array' ) ) );
319+
320+
$this->assertEquals( 'object', $user->getMetaData()->get( 'object' )->type );
321+
$this->assertEquals( 'object', gettype( $user->getMeta( 'object' ) ) );
322+
323+
$this->assertEquals( 'NULL', $user->getMetaData()->get( 'null' )->type );
324+
$this->assertEquals( 'NULL', gettype( $user->getMeta( 'null' ) ) );
325+
326+
$this->assertEquals( 'datetime', $user->getMetaData()->get( 'datetime' )->type );
327+
$this->assertInstanceOf( DateTime::class, $user->getMeta( 'datetime' ) );
328+
329+
$this->assertEquals( 'model', $user->getMetaData()->get( 'model' )->type );
330+
$this->assertInstanceOf( User::class, $user->getMeta( 'model' ) );
331+
$this->assertEquals( $user2->id, $user->getMeta( 'model' )->id );
332+
333+
$hash1 = spl_object_hash( $user->getMeta( 'model' ) );
334+
$hash2 = spl_object_hash( $user->getMeta( 'model' ) );
335+
$this->assertEquals( $hash1, $hash2 );
336+
337+
$user2->delete();
338+
// reload user
339+
$user = User::find( $user->id );
340+
341+
$this->expectException( ModelNotFoundException::class );
342+
$user->getMeta( 'model' );
343+
344+
$user->delete();
345+
}
346+
}

‎tests/Models/User.php

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Models;
4+
5+
use Kodeine\Metable\Metable;
6+
use Illuminate\Events\Dispatcher;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\HasOne;
9+
10+
class User extends Model
11+
{
12+
use Metable;
13+
14+
public $defaultMetaValues = [
15+
'default_meta_key' => 'default_meta_value',
16+
];
17+
18+
public $hideMeta = false;
19+
20+
public $disableFluentMeta = false;
21+
22+
/**
23+
* This is dummy relation to itself.
24+
*
25+
* @return HasOne
26+
*/
27+
public function dummy(): HasOne {
28+
return $this->hasOne( static::class, 'user_id', 'id' );
29+
}
30+
31+
public function getAccessorMeta($value): string {
32+
return 'accessed_' . $value;
33+
}
34+
35+
public function setMutatorMeta($value) {
36+
$this->setMeta( 'mutator', 'mutated_' . $value );
37+
}
38+
39+
public static function boot() {
40+
41+
static::setEventDispatcher( new Dispatcher() );
42+
parent::boot();
43+
}
44+
}

0 commit comments

Comments
 (0)
Please sign in to comment.