├── tests
└── .gitkeep
├── .gitignore
├── TODO.md
├── .travis.yml
├── views
├── base
│ └── bootstrap.blade.php
└── inputs
│ ├── string.blade.php
│ ├── textarea.blade.php
│ ├── radios.blade.php
│ └── checkboxes.blade.php
├── src
├── Exceptions
│ ├── ResourceKeyNotSpecifiedExceptionException.php
│ ├── InvalidCacheDriverException.php
│ ├── ResourceDescriptorNotDefinedException.php
│ ├── ResourceNotDefinedException.php
│ ├── ResourceDescriptorChoicesNotDefinedException.php
│ └── ResourceDescriptorNameNotDefinedException.php
├── Facades
│ ├── Resource.php
│ └── ResourceGroup.php
├── Descriptors
│ ├── Text.php
│ ├── String.php
│ ├── ChooseOne.php
│ └── ChooseMany.php
├── Contracts
│ ├── StorageInterface.php
│ └── DescriptorInterface.php
├── Traits
│ ├── PlainStorage.php
│ ├── SerializedStorage.php
│ └── ChooseableDescriptor.php
├── Commands
│ ├── stubs
│ │ ├── CreateResourcesTable.php
│ │ └── CreateResourceTranslationsTable.php
│ ├── TableCommand.php
│ └── ImportCommand.php
├── Models
│ ├── ResourceTranslation.php
│ └── Resource.php
├── ServiceProvider.php
├── Descriptor.php
├── ResourceGroup.php
└── Resource.php
├── phpunit.xml
├── composer.json
├── config
└── resources.php
├── LICENSE
└── README.md
/tests/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /vendor/
3 | /composer.lock
4 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | settings (id, namespace, name, value, description, group_id, is_translateable)
2 | setting_translations( id, setting_id, locale, description, value )
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - hhvm
8 |
9 | before_script:
10 | - composer self-update
11 | - composer install --prefer-source --no-interaction --dev
12 |
13 | script: phpunit
14 |
--------------------------------------------------------------------------------
/views/base/bootstrap.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | @yield('input.content')
4 |
5 | @if ($errors->has($id))
6 | {{$errors->first($id)}}
7 | @endif
8 |
9 |
--------------------------------------------------------------------------------
/src/Exceptions/ResourceKeyNotSpecifiedExceptionException.php:
--------------------------------------------------------------------------------
1 | {{ $name }}
5 |
6 | @overwrite
7 |
--------------------------------------------------------------------------------
/views/inputs/textarea.blade.php:
--------------------------------------------------------------------------------
1 | @extends('resources::base.bootstrap')
2 |
3 | @section('input.content')
4 |
5 |
6 | @overwrite
7 |
--------------------------------------------------------------------------------
/src/Exceptions/InvalidCacheDriverException.php:
--------------------------------------------------------------------------------
1 | {{ $name }}
5 | @foreach( $choices as $_key => $_choice )
6 |
7 |
15 |
16 | @endforeach
17 | @overwrite
18 |
--------------------------------------------------------------------------------
/src/Descriptors/ChooseOne.php:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./tests/
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Traits/PlainStorage.php:
--------------------------------------------------------------------------------
1 | =5.4",
12 | "illuminate/redis": "~5",
13 | "illuminate/console": "~5",
14 | "illuminate/database": "~5",
15 | "illuminate/support": "~5"
16 | },
17 | "require-dev": {
18 | "phpunit/phpunit": "~4.0"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Cviebrock\\LaravelResources\\": "src"
23 | }
24 | },
25 | "minimum-stability": "dev"
26 | }
27 |
--------------------------------------------------------------------------------
/src/Traits/SerializedStorage.php:
--------------------------------------------------------------------------------
1 | {{ $name }}
5 | {{-- hidden element to force the input to be there, even if all the checkboxes are empty --}}
6 |
7 | @foreach( $choices as $_key => $_choice )
8 |
9 |
17 |
18 | @endforeach
19 | @overwrite
20 |
--------------------------------------------------------------------------------
/src/Exceptions/ResourceDescriptorNotDefinedException.php:
--------------------------------------------------------------------------------
1 | key;
20 | }
21 |
22 |
23 | /**
24 | * Set the key name.
25 | *
26 | * @param $key
27 | * @return $this
28 | */
29 |
30 | public function setKey($key) {
31 | $this->key = $key;
32 | $this->message = "Resource descriptor not found for [{$key}].";
33 |
34 | return $this;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Commands/stubs/CreateResourcesTable.php:
--------------------------------------------------------------------------------
1 | increments('resource_id');
17 | $table->string('resource_key');
18 | $table->string('resource_class');
19 | $table->timestamps();
20 | $table->softDeletes();
21 |
22 | $table->unique('resource_key');
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::dropIfExists('%PREFIX%resources');
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/Models/ResourceTranslation.php:
--------------------------------------------------------------------------------
1 | setTable($prefix . 'resource_translations');
24 |
25 | parent::__construct($attributes);
26 | }
27 |
28 |
29 | /**
30 | * Relationship with Resource Model
31 | *
32 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
33 | */
34 | public function resource() {
35 | return $this->belongsTo('Resource');
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/config/resources.php:
--------------------------------------------------------------------------------
1 | '',
20 |
21 | /**
22 | * The cache prefix used to store the settings.
23 | */
24 | 'cachePrefix' => 'resources',
25 |
26 | /**
27 | * Here is where you define all the resources your application needs.
28 | * You can make it a nested array, or use dot-notation.
29 | *
30 | * The values of the array represent the resource descriptor classes
31 | * that are used
32 | */
33 | 'resources' => [
34 |
35 | // 'home.title' => 'My App'
36 | ]
37 |
38 | ];
39 |
--------------------------------------------------------------------------------
/src/Commands/stubs/CreateResourceTranslationsTable.php:
--------------------------------------------------------------------------------
1 | increments('resource_translation_id');
17 | $table->unsignedInteger('resource_id');
18 | $table->string('locale', 10);
19 | $table->text('value')->nullable()->default(null);
20 | $table->timestamps();
21 | $table->softDeletes();
22 |
23 | $table->index('locale');
24 | $table->unique(['resource_id','locale']);
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down()
34 | {
35 | Schema::dropIfExists('%PREFIX%resource_translations');
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/Exceptions/ResourceNotDefinedException.php:
--------------------------------------------------------------------------------
1 | key = $key;
29 | $this->locale = $locale;
30 | $this->message = "Resource not found for [{$locale}:{$key}].";
31 |
32 | return $this;
33 | }
34 |
35 |
36 | /**
37 | * @return mixed
38 | */
39 | public function getLocale() {
40 | return $this->locale;
41 | }
42 |
43 |
44 | /**
45 | * Get the key name
46 | *
47 | * @return string
48 | */
49 | public function getKey() {
50 | return $this->key;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Exceptions/ResourceDescriptorChoicesNotDefinedException.php:
--------------------------------------------------------------------------------
1 | locale;
25 | }
26 |
27 |
28 | /**
29 | * Get the key name.
30 | *
31 | * @return string
32 | */
33 | public function getKey() {
34 | return $this->key;
35 | }
36 |
37 |
38 | /**
39 | * Set the error reference.
40 | *
41 | * @param $key
42 | * @param $locale
43 | * @return $this
44 | */
45 | public function setReference($key, $locale) {
46 | $this->key = $key;
47 | $this->locale = $locale;
48 | $this->message = "Resource choices not defined for [{$locale}:{$key}].";
49 |
50 | return $this;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Exceptions/ResourceDescriptorNameNotDefinedException.php:
--------------------------------------------------------------------------------
1 | key = $key;
29 | $this->descriptorClass = $descriptorClass;
30 | $this->message = "Resource descriptor [{$descriptorClass}] not found for [{$key}].";
31 |
32 | return $this;
33 | }
34 |
35 |
36 | /**
37 | * @return string
38 | */
39 | public function getKey() {
40 | return $this->key;
41 | }
42 |
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getDescriptorClass() {
48 | return $this->descriptorClass;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Traits/ChooseableDescriptor.php:
--------------------------------------------------------------------------------
1 | getChoices();
20 |
21 | return $data;
22 | }
23 |
24 |
25 | /**
26 | * Return the list of choices available for the descriptor.
27 | *
28 | * @return array
29 | * @throws ResourceDescriptorChoicesNotDefinedException
30 | */
31 | public function getChoices() {
32 |
33 | $locale = $this->getLocale();
34 | $choices = array_get($this->choiceValues, $locale);
35 |
36 | if (!$choices) {
37 | throw (new ResourceDescriptorChoicesNotDefinedException)->setReference(get_called_class(), $locale);
38 | }
39 |
40 | return $choices;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Colin Viebrock
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # laravel-resources
2 |
3 | > Note: The `master` branch and `>=1.0` releases are for Laravel 5.
4 | > Use the `0.x` branch and `~0.9` releases for Laravel 4.
5 |
6 |
7 |
8 | ## Installation
9 |
10 | Include the package:
11 |
12 | ```sh
13 | composer require "cviebrock/laravel-resources:~0.9"
14 | ```
15 |
16 | Add service provider and facades to `app/config.php`:
17 |
18 | ```php
19 | 'providers' => [
20 | 'Cviebrock\LaravelResources\ServiceProvider',
21 | ],
22 | 'aliases' => [
23 | 'Resource' => 'Cviebrock\LaravelResources\Facades\Resource',
24 | 'ResourceGroup' => 'Cviebrock\LaravelResources\Facades\ResourceGroup',
25 | ]
26 | ```
27 |
28 | Publish the configuration:
29 |
30 | ```sh
31 | php artisan config:publish "cviebrock/laravel-resources"
32 | ```
33 |
34 | Edit the configuration (if needed), then generate and run the migration:
35 |
36 | ```sh
37 | php artisan resources:table
38 | php artisan migrate
39 | ```
40 |
41 | ## Configuration
42 |
43 | Update `app/config/packages/cviebrock/laravel-resources/resources.php` with the array of keys/descriptor classes you need.
44 |
45 | Then, run the initial import to load those values in to the database:
46 |
47 | ```sh
48 | php artisan resources:import
49 | ```
50 |
--------------------------------------------------------------------------------
/src/Models/Resource.php:
--------------------------------------------------------------------------------
1 | setTable($prefix . 'resources');
26 |
27 | parent::__construct($attributes);
28 | }
29 |
30 |
31 | /**
32 | * Find the first model with the given value for "key"
33 | *
34 | * @param $key string
35 | * @return mixed
36 | */
37 | public static function firstByKey($key) {
38 | return static::where('resource_key', $key)->first();
39 | }
40 |
41 |
42 | /**
43 | * Relationship with ResourceTranslations models.
44 | *
45 | * @return \Illuminate\Database\Eloquent\Relations\HasMany
46 | */
47 | public function translations() {
48 | return $this->hasMany('Cviebrock\LaravelResources\Models\ResourceTranslation');
49 | }
50 |
51 |
52 | public function findTranslation($locale) {
53 | return $this->translations->first(function ($idx, $item) use ($locale) {
54 | return $item->locale === $locale;
55 | });
56 | }
57 | }
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/Contracts/DescriptorInterface.php:
--------------------------------------------------------------------------------
1 | info('Creating package migrations ...');
39 | foreach ($this->stubs as $stub) {
40 | $fullPath = $this->createMigration($stub);
41 | file_put_contents($fullPath, $this->getMigrationStub($stub));
42 | $this->comment(basename($fullPath));
43 | }
44 | $this->info('Migrations created successfully!');
45 | $this->call('dump-autoload');
46 | $this->info('Don\'t forget to run "artisan migrate".');
47 | }
48 |
49 |
50 | /**
51 | * Create a base migration file for the settings table.
52 | *
53 | * @param string $stub
54 | * @return string
55 | */
56 | protected function createMigration($stub) {
57 | $path = $this->laravel['path'] . '/database/migrations';
58 |
59 | return $this->laravel['migration.creator']->create($stub, $path);
60 | }
61 |
62 |
63 | /**
64 | * Get the contents of the migration stub and insert the correct table name.
65 | *
66 | * @param string $stub
67 | * @return string
68 | */
69 | protected function getMigrationStub($stub) {
70 | $className = studly_case($stub);
71 | $data = file_get_contents(__DIR__ . '/stubs/' . $className . '.php');
72 |
73 | return str_replace(
74 | '%PREFIX%',
75 | Config::get('resources.tablePrefix', ''),
76 | $data
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Commands/ImportCommand.php:
--------------------------------------------------------------------------------
1 | option('force');
33 | $this->info('Importing resources' . ($force ? ' (with force)' : ''));
34 |
35 | $allResources = array_dot(Config::get('resources.resources', []));
36 |
37 | foreach ($allResources as $key => $descriptorClass) {
38 |
39 | $resource = $this->laravel['resources.resource']->key($key);
40 |
41 | foreach ($resource->getDescriptor()->getSeedValues() as $locale => $value) {
42 |
43 | $resource->locale($locale);
44 |
45 | try {
46 | $exists = ($resource->getFromDB() !== null);
47 | } catch (ResourceNotDefinedException $e) {
48 | $exists = false;
49 | }
50 |
51 | if ($force || !$exists) {
52 | $resource->setValue($value);
53 | $this->comment('Settting key [' . $resource->getLocalizedKey() . ']');
54 | } else {
55 | $this->comment('Skipping key [' . $resource->getLocalizedKey() . ']');
56 | }
57 | }
58 | }
59 |
60 | $this->info('Resources imported!');
61 |
62 | if ($this->option('clear')) {
63 | $this->info('Clearing undefined resources');
64 | $keys = array_keys($allResources);
65 | $unusedResources = Resource::whereNotIn('resource_key', $keys)->get();
66 |
67 | if ($unusedResources->count()) {
68 | foreach ($unusedResources as $unusedResource) {
69 | $key = $unusedResource->getAttribute('resource_key');
70 | $unusedResource->translations()->forceDelete();
71 | $unusedResource->forceDelete();
72 | $this->comment('Deleting resource [' . $key . ']');
73 | }
74 | } else {
75 | $this->comment('No unused resources found.');
76 | }
77 | }
78 | }
79 |
80 |
81 | /**
82 | * Get the console command options.
83 | *
84 | * @return array
85 | */
86 | protected function getOptions() {
87 |
88 | return [
89 | ['force', '-f', InputOption::VALUE_NONE, 'Overwrite existing keys with data from configuration files.'],
90 | ['clear', '-c', InputOption::VALUE_NONE, 'Clear out unused keys from database (i.e. keys not defined in configuration files).'],
91 | ];
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/ServiceProvider.php:
--------------------------------------------------------------------------------
1 | handleConfigs();
26 | $this->handleViews();
27 | }
28 |
29 |
30 | /**
31 | * Register the service provider.
32 | *
33 | * @return void
34 | */
35 | public function register() {
36 | $this->registerResource();
37 | $this->registerResourceGroup();
38 | $this->registerCommands();
39 | }
40 |
41 |
42 | /**
43 | * Register the Resource
44 | */
45 | private function registerResource() {
46 | $this->app->bind('resources.resource', function ($app) {
47 |
48 | $cache = $app['cache'];
49 | $store = $cache->driver()->getStore();
50 | if (!is_subclass_of($store, 'Illuminate\Cache\TaggableStore')) {
51 | throw new InvalidCacheDriverException;
52 | }
53 |
54 | return new Resource($cache);
55 | });
56 | }
57 |
58 |
59 | /**
60 | * Register the ResourceGroup
61 | */
62 | private function registerResourceGroup() {
63 | $this->app->bind('resources.group', function ($app) {
64 |
65 | return new ResourceGroup();
66 | });
67 | }
68 |
69 |
70 | /**
71 | * Register the Commands
72 | */
73 | private function registerCommands() {
74 | $this->app['resources.command.table'] = $this->app->share(function ($app) {
75 | return new TableCommand();
76 | });
77 |
78 | $this->commands('resources.command.table');
79 |
80 | $this->app['resources.command.import'] = $this->app->share(function ($app) {
81 | return new ImportCommand();
82 | });
83 |
84 | $this->commands('resources.command.import');
85 | }
86 |
87 |
88 | /**
89 | * Get the services provided by the provider.
90 | *
91 | * @return array
92 | */
93 | public function provides() {
94 | return [
95 | 'resources.resource',
96 | 'resources.group',
97 | 'resources.command.table',
98 | 'resources.command.populate'
99 | ];
100 | }
101 |
102 | private function handleConfigs() {
103 |
104 | $configPath = __DIR__ . '/../config/resources.php';
105 | $this->publishes([$configPath => config_path('resources.php')]);
106 | $this->mergeConfigFrom($configPath, 'resources');
107 | }
108 |
109 | private function handleViews() {
110 |
111 | $this->loadViewsFrom( __DIR__.'/../views', 'resources');
112 | $this->publishes([__DIR__.'/../views' => base_path('resources/views/vendor/resources')]);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/Descriptor.php:
--------------------------------------------------------------------------------
1 | validator = $validator;
75 | }
76 |
77 |
78 | /**
79 | * Get the value of the resource descriptor.
80 | *
81 | * @return mixed
82 | */
83 | public function getValue() {
84 |
85 | return $this->value;
86 | }
87 |
88 |
89 | /**
90 | * Set the value of the resource descriptor.
91 | *
92 | * @param $value
93 | * @return mixed|void
94 | */
95 | public function setValue($value) {
96 |
97 | $this->value = $value;
98 | }
99 |
100 |
101 | /**
102 | * @return string
103 | */
104 | public function getDescription() {
105 |
106 | return $this->description;
107 | }
108 |
109 |
110 | /**
111 | * @return array
112 | */
113 | public function getSeedValues() {
114 |
115 | return $this->seedValues;
116 | }
117 |
118 |
119 | /**
120 | * Render the descriptor as a form input.
121 | *
122 | * @param mixed $value
123 | * @return mixed
124 | */
125 | public function renderInput($value) {
126 |
127 | $data = $this->getInputData($value);
128 |
129 | return View::make($this->template, $data)->render();
130 | }
131 |
132 |
133 | /**
134 | * @param $value
135 | * @return array
136 | */
137 | protected function getInputData($value) {
138 |
139 | $data = [
140 | 'name' => $this->getName(),
141 | 'description' => $this->getDescription(),
142 | 'id' => $this->key,
143 | 'fieldName' => 'resources[' . $this->key . ']',
144 | 'value' => $value
145 | ];
146 |
147 | return $data;
148 | }
149 |
150 |
151 | /**
152 | * Get the name for the resource.
153 | *
154 | * @return mixed
155 | */
156 | public function getName() {
157 |
158 | if (!$this->name) {
159 | throw (new ResourceDescriptorNameNotDefinedException)->setReference(get_called_class());
160 | }
161 |
162 | return $this->name;
163 | }
164 |
165 |
166 | /**
167 | * Validate value against rules.
168 | *
169 | * @param $value
170 | * @return bool|MessageBag
171 | */
172 | public function validate($value) {
173 |
174 | $key = $this->getKey();
175 |
176 | $validator = app('validator')->make(
177 | [$key => $value],
178 | [$key => $this->getValidationRules()],
179 | [$this->getValidationMessages()]
180 | );
181 |
182 | if ($validator->passes()) {
183 | return true;
184 | }
185 |
186 | return $validator->messages();
187 | }
188 |
189 |
190 | /**
191 | * @return mixed
192 | */
193 | public function getKey() {
194 | return $this->key;
195 | }
196 |
197 |
198 | /**
199 | * Set the resource key.
200 | *
201 | * @param mixed $key
202 | */
203 | public function setKey($key) {
204 | $this->key = $key;
205 | }
206 |
207 |
208 | /**
209 | * @return array
210 | */
211 | public function getValidationRules() {
212 | return $this->validationRules;
213 | }
214 |
215 |
216 | /**
217 | * @return array
218 | */
219 | public function getValidationMessages() {
220 | return $this->validationMessages;
221 | }
222 |
223 |
224 | /**
225 | * @return string
226 | */
227 | public function getLocale() {
228 | return $this->locale;
229 | }
230 |
231 |
232 | /**
233 | * Set the resource locale.
234 | *
235 | * @param string $locale
236 | */
237 | public function setLocale($locale) {
238 | $this->locale = $locale;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/ResourceGroup.php:
--------------------------------------------------------------------------------
1 | resources;
44 | }
45 |
46 |
47 | /**
48 | * @return MessageBag|null
49 | */
50 | public function getErrors() {
51 | return $this->errors;
52 | }
53 |
54 |
55 | /**
56 | * @return mixed
57 | */
58 | public function getData() {
59 | return $this->data;
60 | }
61 |
62 |
63 | /**
64 | * @param mixed $data
65 | */
66 | public function setData($data) {
67 | $this->data = $data;
68 | }
69 |
70 |
71 | /**
72 | * Set the locale.
73 | *
74 | * @param string $locale
75 | * @return $this
76 | */
77 | public function locale($locale) {
78 | $this->locale = $locale;
79 |
80 | return $this;
81 | }
82 |
83 |
84 | /**
85 | * Get a collection of all the resources whose keys match the given
86 | * pattern.
87 | *
88 | * @param string $pattern
89 | * @return Collection
90 | */
91 | public function getByPattern($pattern = '*') {
92 |
93 | $resourceKeys = array_keys($this->getResourceMap());
94 |
95 | if ($pattern !== '*') {
96 | $resourceKeys = array_filter($resourceKeys, function ($key) use ($pattern) {
97 | return starts_with($key, $pattern);
98 | });
99 | }
100 |
101 | return $this->getByKeys($resourceKeys, true);
102 | }
103 |
104 |
105 | /**
106 | * Get the resource map, or load from config.
107 | *
108 | * @return array
109 | */
110 | public function getResourceMap() {
111 |
112 | if (!($this->resourceMap)) {
113 | $this->resourceMap = array_dot(Config::get('resources.resources'));
114 | }
115 |
116 | return $this->resourceMap;
117 | }
118 |
119 |
120 | /**
121 | * Get a collection of all the resources with the given keys,
122 | * optionally sorted into the same order as defined in the resource map.
123 | *
124 | * @param array $keys
125 | * @param bool $sort
126 | * @return Collection
127 | */
128 | public function getByKeys(array $keys, $sort = false) {
129 |
130 | $this->resources = new Collection();
131 |
132 | $locale = $this->getLocale();
133 |
134 | foreach (array_reverse($keys) as $key) {
135 | $this->resources->put($key, app('resources.resource')
136 | ->locale($locale)
137 | ->key($key)
138 | );
139 | }
140 |
141 | if ($sort) {
142 | $this->sortByResourceMap();
143 | }
144 |
145 | return $this;
146 | }
147 |
148 |
149 | /**
150 | * Get the locale, or load from config.
151 | *
152 | * @return string
153 | */
154 | public function getLocale() {
155 | if (!$this->locale) {
156 | $this->locale = \Config::get('app.locale');
157 | }
158 |
159 | return $this->locale;
160 | }
161 |
162 |
163 | /**
164 | * Arrange the order of the resources so they mirror the order in the resources
165 | * config.
166 | */
167 | protected function sortByResourceMap() {
168 |
169 | $resourceMap = $this->getResourceMap();
170 | $sortOrder = array_flip(array_keys($resourceMap));
171 |
172 | $this->resources->sort(function ($a, $b) use ($sortOrder) {
173 | return $sortOrder[$a->getKey()] > $sortOrder[$b->getKey()];
174 | });
175 | }
176 |
177 |
178 | public function validate($input) {
179 |
180 | $rules = $messages = $niceNames = [];
181 |
182 | if (!$this->getResources()) {
183 | $this->getByKeys(array_keys($input));
184 | }
185 |
186 | foreach ($this->getResources() as $key=>$resource) {
187 | $descriptor = $resource->getDescriptor();
188 | if ($resourceRules = $descriptor->getValidationRules()) {
189 | $rules[$key] = $resourceRules;
190 | }
191 | if ($resourceMessages = $descriptor->getValidationMessages()) {
192 | $messages[$key] = $resourceMessages;
193 | }
194 | $niceNames[$key] = $descriptor->getName();
195 | }
196 |
197 | $validator = Validator::make($input, $rules, $messages);
198 | $validator->setAttributeNames($niceNames);
199 |
200 | return $validator;
201 | }
202 |
203 |
204 | public function setValues($input) {
205 | $keys = array_keys($input);
206 | $resources = static::getByKeys($keys);
207 |
208 | foreach ($resources as $key => $resource) {
209 | $value = array_get($input, $key);
210 | $resource->setValue($value);
211 | }
212 |
213 | return true;
214 | }
215 |
216 |
217 | public function get($key) {
218 | return $this->resources->get($key);
219 | }
220 |
221 |
222 | /**
223 | * Get the instance as an array.
224 | *
225 | * @return array
226 | */
227 | public function toArray() {
228 | return $this->resources->toArray();
229 | }
230 |
231 |
232 | /**
233 | * ArrayAccess offsetExists method
234 | *
235 | * @param mixed $offset
236 | * @return bool
237 | */
238 | public function offsetExists($offset) {
239 | return $this->resources->offsetExists($offset);
240 | }
241 |
242 |
243 | /**
244 | * ArrayAccess offsetGet method
245 | *
246 | * @param mixed $offset
247 | * @return mixed
248 | */
249 | public function offsetGet($offset) {
250 | return $this->resources->offsetGet($offset);
251 | }
252 |
253 |
254 | /**
255 | * ArrayAccess offSetSet method
256 | *
257 | * @param mixed $offset
258 | * @param mixed $value
259 | */
260 | public function offsetSet($offset, $value) {
261 | return $this->resources->offsetSet($offset, $value);
262 | }
263 |
264 |
265 | /**
266 | * ArrayAccess offSetUnset method
267 | *
268 | * @param mixed $offset
269 | */
270 | public function offsetUnset($offset) {
271 | return $this->resources->offsetUnset($offset);
272 | }
273 |
274 |
275 | /**
276 | * Count elements of an object
277 | *
278 | * @return int
279 | */
280 | public function count() {
281 | return $this->resources->count();
282 | }
283 |
284 |
285 | /**
286 | * Retrieve an external iterator
287 | *
288 | * @return Traversable
289 | */
290 | public function getIterator() {
291 | return $this->resources->getIterator();
292 | }
293 |
294 | }
295 |
--------------------------------------------------------------------------------
/src/Resource.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
49 | }
50 |
51 |
52 | /**
53 | * Define the resource locale.
54 | *
55 | * @param string $locale
56 | * @return $this
57 | */
58 | public function locale($locale) {
59 |
60 | $this->setLocale($locale);
61 |
62 | return $this;
63 | }
64 |
65 |
66 | /**
67 | * Get the resource value by key.
68 | *
69 | * @param null $key
70 | * @return mixed|null
71 | * @throws ResourceNotDefinedException
72 | */
73 | public function get($key = null) {
74 |
75 | if ($key) {
76 | $this->setKey($key);
77 | }
78 |
79 | return $this->getValue();
80 | }
81 |
82 |
83 | /**
84 | * Get the value of the resource.
85 | *
86 | * @return mixed|null
87 | * @throws ResourceKeyNotSpecifiedException
88 | * @throws ResourceNotDefinedException
89 | */
90 | public function getValue() {
91 |
92 | $value = $this->loadValueFromCache();
93 |
94 | if ($value !== null) {
95 | return $value;
96 | }
97 |
98 | $value = $this->loadValueFromDatabase();
99 |
100 | if ($value !== null) {
101 | $this->storeValueToCache($value);
102 | return $value;
103 | }
104 |
105 | throw (new ResourceNotDefinedException)->setReference($this->getKey(), $this->getLocale());
106 | }
107 |
108 |
109 | /**
110 | * Load value of the resource from cache.
111 | *
112 | * @return mixed|null
113 | */
114 | protected function loadValueFromCache() {
115 |
116 | $cacheKey = $this->getLocalizedCacheKey();
117 | $tags = $this->buildCacheTags($cacheKey);
118 |
119 | $value = $this->cache->tags($tags)->get($cacheKey);
120 |
121 | return $this->getDescriptor()->fromStore($value);
122 | }
123 |
124 |
125 | /**
126 | * Load value of the resource from database.
127 | *
128 | * @return mixed|null
129 | */
130 | protected function loadValueFromDatabase() {
131 |
132 | if (!$translation = $this->findTranslationModel($this->getKey(), $this->getLocale())) {
133 | return null;
134 | }
135 |
136 | $value = $translation->getAttribute('value');
137 |
138 | return $this->getDescriptor()->fromStore($value);
139 | }
140 |
141 |
142 | /**
143 | * Store a value to the cache.
144 | *
145 | * @param $value
146 | * @return mixed
147 | */
148 | protected function storeValueToCache($value) {
149 |
150 | $cacheKey = $this->getLocalizedCacheKey();
151 | $tags = $this->buildCacheTags($cacheKey);
152 | $storedValue = $this->getDescriptor()->toStore($value);
153 |
154 | return $this->cache->tags($tags)->forever($cacheKey, $storedValue);
155 | }
156 |
157 |
158 | /**
159 | * Get the current key for the resource.
160 | *
161 | * @return string
162 | * @throws ResourceKeyNotSpecifiedException
163 | */
164 | public function getKey() {
165 | if (!$this->key) {
166 | throw new ResourceKeyNotSpecifiedException;
167 | }
168 |
169 | return $this->key;
170 | }
171 |
172 |
173 | /**
174 | * Set resource key.
175 | *
176 | * @param string $key
177 | */
178 | public function setKey($key) {
179 |
180 | $this->key = $key;
181 | $this->clearDescriptor();
182 | }
183 |
184 |
185 | /**
186 | * Get the current locale for the resource, or load from config.
187 | *
188 | * @return string
189 | */
190 | public function getLocale() {
191 | if (!$this->locale) {
192 | $this->locale = Config::get('app.locale', 'en');
193 | }
194 |
195 | return $this->locale;
196 | }
197 |
198 |
199 | /**
200 | * Set resource locale.
201 | *
202 | * @param string $locale
203 | * @return $this
204 | */
205 | public function setLocale($locale) {
206 |
207 | $this->locale = $locale;
208 | $this->clearDescriptor();
209 | }
210 |
211 |
212 | /**
213 | * Build key used for cache storage/lookup.
214 | *
215 | * @return string
216 | * @throws ResourceKeyNotSpecifiedException
217 | */
218 | protected function getLocalizedCacheKey() {
219 |
220 | $cacheKey = $this->getLocalizedKey();
221 | if ($cachePrefix = Config::get('resources.cachePrefix')) {
222 | $cacheKey = $cachePrefix . '.' . $cacheKey;
223 | }
224 |
225 | return $cacheKey;
226 | }
227 |
228 |
229 | /**
230 | * Build an array of key tags from the cache key.
231 | *
232 | * For example, the key "resources.en.homepage.title" will get converted into the array:
233 | *
234 | * [
235 | * 'resources',
236 | * 'resources.en',
237 | * 'resources.en.homepage',
238 | * 'resources.en.homepage.title'
239 | * ]
240 | *
241 | * This will allow us to expire portions of the cache selectively (e.g. per locale).
242 | *
243 | * @param $key
244 | * @return array
245 | */
246 | protected function buildCacheTags($key) {
247 |
248 | $tags = [];
249 | $offset = 0;
250 |
251 | while ($pos = strpos($key, '.', $offset)) {
252 | $tags[] = substr($key, 0, $pos);
253 | $offset = $pos + 1;
254 | }
255 |
256 | $tags[] = $key;
257 |
258 | return $tags;
259 | }
260 |
261 |
262 | /**
263 | * Get the resource descriptor class.
264 | *
265 | * @return Descriptor
266 | * @throws ResourceDescriptorNotDefinedException
267 | */
268 | public function getDescriptor() {
269 |
270 | if (!$this->descriptor) {
271 | if (!$class = $this->getDescriptorClass()) {
272 | throw (new ResourceDescriptorNotDefinedException)->setKey($this->getKey());
273 | }
274 |
275 | $this->descriptor = app($class);
276 | $this->descriptor->setKey($this->getKey());
277 | $this->descriptor->setLocale($this->getLocale());
278 | }
279 |
280 | return $this->descriptor;
281 | }
282 |
283 |
284 | /**
285 | * Load the translation model for a given key and locale
286 | *
287 | * @param $key
288 | * @param $locale
289 | * @return ResourceTranslation|null
290 | */
291 | protected function findTranslationModel($key, $locale) {
292 |
293 | if (!$record = $this->findResourceModel($key)) {
294 | return null;
295 | }
296 |
297 | if (!$translation = $record->findTranslation($locale)) {
298 | return null;
299 | }
300 |
301 | return $translation;
302 | }
303 |
304 |
305 | /**
306 | * Build the localized key for the resource (locale + key)
307 | *
308 | * @return string
309 | * @throws ResourceKeyNotSpecifiedException
310 | */
311 | public function getLocalizedKey() {
312 |
313 | if (!$this->key) {
314 | throw new ResourceKeyNotSpecifiedException;
315 | }
316 |
317 | return $this->locale . '.' . $this->key;
318 | }
319 |
320 |
321 | protected function getDescriptorClass() {
322 | return array_get($this->getResourceMap(), $this->getKey(), null);
323 | }
324 |
325 |
326 | /**
327 | * Load the resource model for a given key.
328 | *
329 | * @param $key
330 | * @return ResourceModel|null
331 | */
332 | protected function findResourceModel($key) {
333 |
334 | return ResourceModel::firstByKey($key);
335 | }
336 |
337 |
338 | /**
339 | * Get the resource map, or load from config.
340 | *
341 | * @return array
342 | */
343 | public function getResourceMap() {
344 | if (!$this->resourceMap) {
345 | $this->resourceMap = array_dot(Config::get('resources.resources'));
346 | }
347 |
348 | return $this->resourceMap;
349 | }
350 |
351 |
352 | public function getFromDB($key = null) {
353 |
354 | if ($key) {
355 | $this->key($key);
356 | }
357 |
358 | return $this->loadValueFromDatabase();
359 | }
360 |
361 |
362 | /**
363 | * Define the resource key, which loads the appropriate descriptor class.
364 | *
365 | * @param string $key
366 | * @return $this
367 | */
368 | public function key($key) {
369 |
370 | $this->setKey($key);
371 |
372 | return $this;
373 | }
374 |
375 |
376 | public function set($key, $value) {
377 |
378 | $this->key($key);
379 |
380 | return $this->setValue($value);
381 | }
382 |
383 |
384 | public function setValue($value) {
385 |
386 | $this->storeValueToDatabase($value);
387 | $this->storeValueToCache($value);
388 |
389 | return $this;
390 | }
391 |
392 |
393 | protected function storeValueToDatabase($value) {
394 |
395 | $record = $this->findResourceModel($this->getKey());
396 |
397 | if (!$record) {
398 | $record = ResourceModel::create([
399 | 'resource_key' => $this->getKey(),
400 | 'resource_class' => get_class($this->getDescriptor()),
401 | ]);
402 | }
403 |
404 | $translation = $this->findTranslationModel($this->getKey(), $this->getLocale());
405 | $storedValue = $this->getDescriptor()->toStore($value);
406 |
407 | if ($translation) {
408 | $translation->update([
409 | 'value' => $storedValue
410 | ]);
411 | } else {
412 | $translation = new ResourceTranslation([
413 | 'locale' => $this->getLocale(),
414 | 'value' => $storedValue
415 | ]);
416 | $record->translations()->save($translation);
417 | }
418 |
419 | return true;
420 | }
421 |
422 |
423 | public function renderInput() {
424 |
425 | $value = array_get(Input::old('resources'), $this->getKey(), $this->getValue());
426 |
427 | return $this->getDescriptor()->renderInput($value);
428 | }
429 |
430 |
431 | public function validate($value) {
432 | return $this->getDescriptor()->validate($value);
433 | }
434 |
435 |
436 | private function clearDescriptor() {
437 | $this->descriptor = null;
438 | }
439 |
440 | }
441 |
--------------------------------------------------------------------------------