From 8fbfa66059e1e38eff3dcae64dc0832df258ad8e Mon Sep 17 00:00:00 2001 From: Jonathan Reinink Date: Fri, 9 Aug 2019 11:33:47 -0400 Subject: [PATCH 1/2] Add user photos --- app/Http/Controllers/ImagesController.php | 13 + app/Http/Controllers/UsersController.php | 33 +- app/Providers/AppServiceProvider.php | 22 +- app/User.php | 11 + composer.json | 1 + composer.lock | 311 +++++++++++++++++- .../2019_03_05_000000_create_users_table.php | 1 + resources/js/Pages/Users/Create.vue | 23 +- resources/js/Pages/Users/Edit.vue | 29 +- resources/js/Pages/Users/Index.vue | 1 + resources/js/Shared/FileInput.vue | 62 ++++ routes/web.php | 3 + 12 files changed, 482 insertions(+), 28 deletions(-) create mode 100644 app/Http/Controllers/ImagesController.php create mode 100644 resources/js/Shared/FileInput.vue diff --git a/app/Http/Controllers/ImagesController.php b/app/Http/Controllers/ImagesController.php new file mode 100644 index 0000000..d3c7c8f --- /dev/null +++ b/app/Http/Controllers/ImagesController.php @@ -0,0 +1,13 @@ +fromRequest()->response(); + } +} diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php index 235dc87..091fb29 100644 --- a/app/Http/Controllers/UsersController.php +++ b/app/Http/Controllers/UsersController.php @@ -25,6 +25,7 @@ class UsersController extends Controller 'name' => $user->name, 'email' => $user->email, 'owner' => $user->owner, + 'photo' => $user->photoUrl(['w' => 40, 'h' => 40, 'fit' => 'crop']), 'deleted_at' => $user->deleted_at, ]; }), @@ -38,15 +39,23 @@ class UsersController extends Controller public function store() { - Auth::user()->account->users()->create( - Request::validate([ - 'first_name' => ['required', 'max:50'], - 'last_name' => ['required', 'max:50'], - 'email' => ['required', 'max:50', 'email', Rule::unique('users')], - 'password' => ['nullable'], - 'owner' => ['required', 'boolean'], - ]) - ); + Request::validate([ + 'first_name' => ['required', 'max:50'], + 'last_name' => ['required', 'max:50'], + 'email' => ['required', 'max:50', 'email', Rule::unique('users')], + 'password' => ['nullable'], + 'owner' => ['required', 'boolean'], + 'photo' => ['nullable', 'image'], + ]); + + Auth::user()->account->users()->create([ + 'first_name' => Request::get('first_name'), + 'last_name' => Request::get('last_name'), + 'email' => Request::get('email'), + 'password' => Request::get('password'), + 'owner' => Request::get('owner'), + 'photo_path' => Request::file('photo') ? Request::file('photo')->store('users') : null, + ]); return Redirect::route('users')->with('success', 'User created.'); } @@ -60,6 +69,7 @@ class UsersController extends Controller 'last_name' => $user->last_name, 'email' => $user->email, 'owner' => $user->owner, + 'photo' => $user->photoUrl(['w' => 60, 'h' => 60, 'fit' => 'crop']), 'deleted_at' => $user->deleted_at, ], ]); @@ -73,10 +83,15 @@ class UsersController extends Controller 'email' => ['required', 'max:50', 'email', Rule::unique('users')->ignore($user->id)], 'password' => ['nullable'], 'owner' => ['required', 'boolean'], + 'photo' => ['nullable', 'image'], ]); $user->update(Request::only('first_name', 'last_name', 'email', 'owner')); + if (Request::file('photo')) { + $user->update(['photo_path' => Request::file('photo')->store('users')]); + } + if (Request::get('password')) { $user->update(['password' => Request::get('password')]); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 4c9896c..d0492c9 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,14 +3,17 @@ namespace App\Providers; use Inertia\Inertia; +use League\Glide\Server; use Carbon\CarbonImmutable; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\URL; use Illuminate\Pagination\UrlWindow; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Session; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\ServiceProvider; use Illuminate\Pagination\LengthAwarePaginator; @@ -22,6 +25,13 @@ class AppServiceProvider extends ServiceProvider } public function register() + { + $this->registerInertia(); + $this->registerGlide(); + $this->registerLengthAwarePaginator(); + } + + public function registerInertia() { Inertia::version(function () { return md5_file(public_path('mix-manifest.json')); @@ -50,8 +60,18 @@ class AppServiceProvider extends ServiceProvider : (object) [], ]; }); + } - $this->registerLengthAwarePaginator(); + protected function registerGlide() + { + $this->app->bind(Server::class, function ($app) { + return Server::create([ + 'source' => Storage::getDriver(), + 'cache' => Storage::getDriver(), + 'cache_folder' => '.glide-cache', + 'base_url' => URL::to('img'), + ]); + }); } protected function registerLengthAwarePaginator() diff --git a/app/User.php b/app/User.php index ee0e739..6478695 100644 --- a/app/User.php +++ b/app/User.php @@ -2,6 +2,8 @@ namespace App; +use League\Glide\Server; +use Illuminate\Support\Facades\App; use Illuminate\Auth\Authenticatable; use Illuminate\Support\Facades\Hash; use Illuminate\Database\Eloquent\SoftDeletes; @@ -32,6 +34,15 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac $this->attributes['password'] = Hash::make($password); } + public function photoUrl(array $attributes) + { + if (!$this->photo_path) { + return; + } + + return App::make(Server::class)->fromPath($this->photo_path, $attributes)->url(); + } + public function scopeOrderByName($query) { $query->orderBy('last_name')->orderBy('first_name'); diff --git a/composer.json b/composer.json index 20db9bb..5c112e7 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "inertiajs/inertia-laravel": "dev-master", "laravel/framework": "5.8.*", "laravel/tinker": "^1.0", + "league/glide": "2.0.x-dev", "reinink/remember-query-strings": "^0.1.0", "tightenco/ziggy": "^0.6.9" }, diff --git a/composer.lock b/composer.lock index 82d2fe7..0e15a3b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1e43fb8319fde39e81c34881865fe457", + "content-hash": "aafc954371d0b2fa7b41fe7aaaf06002", "packages": [ { "name": "dnoegel/php-xdg-base-dir", @@ -430,18 +430,89 @@ ], "time": "2018-07-12T10:23:15+00:00" }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2019-07-01T23:21:34+00:00" + }, { "name": "inertiajs/inertia-laravel", "version": "dev-master", "source": { "type": "git", "url": "https://github.com/inertiajs/inertia-laravel.git", - "reference": "1f0a6f61f36ab3b9000c4831a3685790a03a7a6f" + "reference": "9e68492dac3bae0e0fc6fe5bfc237b3b8105c755" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/1f0a6f61f36ab3b9000c4831a3685790a03a7a6f", - "reference": "1f0a6f61f36ab3b9000c4831a3685790a03a7a6f", + "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/9e68492dac3bae0e0fc6fe5bfc237b3b8105c755", + "reference": "9e68492dac3bae0e0fc6fe5bfc237b3b8105c755", "shasum": "" }, "require-dev": { @@ -458,7 +529,10 @@ "autoload": { "psr-4": { "Inertia\\": "src" - } + }, + "files": [ + "./helpers.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -476,7 +550,77 @@ "inertia", "laravel" ], - "time": "2019-07-31T11:59:34+00:00" + "time": "2019-08-08T14:19:31+00:00" + }, + { + "name": "intervention/image", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "39eaef720d082ecc54c64bf54541c55f10db546d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/39eaef720d082ecc54c64bf54541c55f10db546d", + "reference": "39eaef720d082ecc54c64bf54541c55f10db546d", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "guzzlehttp/psr7": "~1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "~0.9.2", + "phpunit/phpunit": "^4.8 || ^5.7" + }, + "suggest": { + "ext-gd": "to use GD library based image processing.", + "ext-imagick": "to use Imagick based image processing.", + "intervention/imagecache": "Caching extension for the Intervention Image library" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + }, + "laravel": { + "providers": [ + "Intervention\\Image\\ImageServiceProvider" + ], + "aliases": { + "Image": "Intervention\\Image\\Facades\\Image" + } + } + }, + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@olivervogel.com", + "homepage": "http://olivervogel.com/" + } + ], + "description": "Image handling and manipulation library with support for Laravel integration", + "homepage": "http://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "thumbnail", + "watermark" + ], + "time": "2019-06-24T14:06:31+00:00" }, { "name": "jakub-onderka/php-console-color", @@ -860,6 +1004,68 @@ ], "time": "2019-06-18T20:09:29+00:00" }, + { + "name": "league/glide", + "version": "2.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/glide.git", + "reference": "8879aee54cea888287c9acdabff2cff340b14504" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/glide/zipball/8879aee54cea888287c9acdabff2cff340b14504", + "reference": "8879aee54cea888287c9acdabff2cff340b14504", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.1", + "intervention/image": "^2.1", + "league/flysystem": "^1.0", + "php": "^5.4 | ^7.0", + "symfony/http-foundation": "^2.3|^3.0|^4.0" + }, + "require-dev": { + "mockery/mockery": "~0.9", + "phpunit/php-token-stream": "^1.4", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Glide\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "homepage": "http://reinink.ca" + } + ], + "description": "Wonderfully easy on-demand image manipulation library with an HTTP based API.", + "homepage": "http://glide.thephpleague.com", + "keywords": [ + "ImageMagick", + "editing", + "gd", + "image", + "imagick", + "league", + "manipulation", + "processing" + ], + "time": "2018-06-19T01:25:30+00:00" + }, { "name": "monolog/monolog", "version": "1.24.0", @@ -1261,6 +1467,56 @@ ], "time": "2017-02-14T16:28:37+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, { "name": "psr/log", "version": "1.1.0", @@ -1430,6 +1686,46 @@ ], "time": "2018-10-13T15:16:03+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "ramsey/uuid", "version": "3.8.0", @@ -4922,7 +5218,8 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "inertiajs/inertia-laravel": 20 + "inertiajs/inertia-laravel": 20, + "league/glide": 20 }, "prefer-stable": true, "prefer-lowest": false, diff --git a/database/migrations/2019_03_05_000000_create_users_table.php b/database/migrations/2019_03_05_000000_create_users_table.php index 5859a28..f58beed 100644 --- a/database/migrations/2019_03_05_000000_create_users_table.php +++ b/database/migrations/2019_03_05_000000_create_users_table.php @@ -16,6 +16,7 @@ class CreateUsersTable extends Migration $table->string('email', 50)->unique(); $table->string('password')->nullable(); $table->boolean('owner')->default(false); + $table->string('photo_path', 100)->nullable(); $table->rememberToken(); $table->timestamps(); $table->softDeletes(); diff --git a/resources/js/Pages/Users/Create.vue b/resources/js/Pages/Users/Create.vue index aa9e657..45e0153 100644 --- a/resources/js/Pages/Users/Create.vue +++ b/resources/js/Pages/Users/Create.vue @@ -15,6 +15,7 @@ +
Create User @@ -29,6 +30,7 @@ import Layout from '@/Shared/Layout' import LoadingButton from '@/Shared/LoadingButton' import SelectInput from '@/Shared/SelectInput' import TextInput from '@/Shared/TextInput' +import FileInput from '@/Shared/FileInput' export default { components: { @@ -36,24 +38,35 @@ export default { LoadingButton, SelectInput, TextInput, + FileInput, }, remember: 'form', data() { return { sending: false, form: { - first_name: null, - last_name: null, - email: null, - password: null, + first_name: '', + last_name: '', + email: '', + password: '', owner: false, + photo: '', }, } }, methods: { submit() { this.sending = true - this.$inertia.post(this.route('users.store'), this.form) + + var data = new FormData() + data.append('first_name', this.form.first_name) + data.append('last_name', this.form.last_name) + data.append('email', this.form.email) + data.append('password', this.form.password) + data.append('owner', this.form.owner ? '1' : '0') + data.append('photo', this.form.photo) + + this.$inertia.post(this.route('users.store'), data) .then(() => this.sending = false) }, }, diff --git a/resources/js/Pages/Users/Edit.vue b/resources/js/Pages/Users/Edit.vue index 8ae060d..5f04df5 100644 --- a/resources/js/Pages/Users/Edit.vue +++ b/resources/js/Pages/Users/Edit.vue @@ -1,10 +1,13 @@