├── public ├── favicon.ico ├── robots.txt ├── mix-manifest.json ├── .htaccess ├── web.config ├── index.php └── svg │ └── 404.svg ├── resources ├── lang │ ├── de │ │ ├── .gitkeep │ │ └── messages.php │ ├── es │ │ ├── .gitkeep │ │ └── messages.php │ ├── en │ │ ├── messages.php │ │ ├── pagination.php │ │ ├── auth.php │ │ └── passwords.php │ ├── en.json │ ├── es.json │ └── de.json ├── views │ ├── vendor │ │ └── mail │ │ │ ├── markdown │ │ │ ├── panel.blade.php │ │ │ ├── table.blade.php │ │ │ ├── footer.blade.php │ │ │ ├── promotion.blade.php │ │ │ ├── subcopy.blade.php │ │ │ ├── button.blade.php │ │ │ ├── header.blade.php │ │ │ ├── promotion │ │ │ │ └── button.blade.php │ │ │ ├── layout.blade.php │ │ │ └── message.blade.php │ │ │ └── html │ │ │ ├── table.blade.php │ │ │ ├── header.blade.php │ │ │ ├── subcopy.blade.php │ │ │ ├── promotion.blade.php │ │ │ ├── footer.blade.php │ │ │ ├── panel.blade.php │ │ │ ├── promotion │ │ │ └── button.blade.php │ │ │ ├── message.blade.php │ │ │ ├── button.blade.php │ │ │ └── layout.blade.php │ ├── secret.blade.php │ ├── components │ │ ├── badge.blade.php │ │ ├── tags.blade.php │ │ ├── errors.blade.php │ │ ├── comment-list.blade.php │ │ ├── updated.blade.php │ │ ├── comment-form.blade.php │ │ └── card.blade.php │ ├── emails │ │ └── posts │ │ │ ├── blog-post-added.blade.php │ │ │ ├── comment-posted-on-watched.blade.php │ │ │ ├── commented-markdown.blade.php │ │ │ └── commented.blade.php │ ├── contact.blade.php │ ├── posts │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ ├── _form.blade.php │ │ ├── _activity.blade.php │ │ ├── show.blade.php │ │ └── index.blade.php │ ├── home.blade.php │ ├── users │ │ ├── show.blade.php │ │ └── edit.blade.php │ ├── auth │ │ ├── login.blade.php │ │ └── register.blade.php │ └── layout.blade.php ├── sass │ ├── _variables.scss │ └── app.scss └── js │ ├── components │ └── ExampleComponent.vue │ ├── app.js │ └── bootstrap.js ├── database ├── .gitignore ├── factories │ ├── ProfileFactory.php │ ├── CommentFactory.php │ ├── BlogPostFactory.php │ ├── AuthorFactory.php │ └── UserFactory.php ├── seeds │ ├── UsersTableSeeder.php │ ├── TagsTableSeeder.php │ ├── BlogPostsTableSeeder.php │ ├── DatabaseSeeder.php │ ├── BlogPostTagTableSeeder.php │ └── CommentsTableSeeder.php └── migrations │ ├── 2019_01_22_173935_change_blogposts_table_name.php │ ├── 2019_02_25_124726_create_authors_table.php │ ├── 2019_01_20_161408_create_blogposts_table.php │ ├── 2019_04_20_154128_create_tags_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2019_04_04_162648_add_soft_deletes_to_comments_table.php │ ├── 2019_05_14_141217_add_locale_to_users_table.php │ ├── 2019_04_02_163816_add_soft_deletes_to_blog_posts_table.php │ ├── 2019_04_06_145253_add_is_admin_to_users_table.php │ ├── 2019_04_28_104117_create_images_table.php │ ├── 2019_02_25_124736_create_profiles_table.php │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2019_07_28_093515_add_api_token_to_users_table.php │ ├── 2019_05_01_114040_add_polymorph_to_images_table.php │ ├── 2019_05_09_170210_create_failed_jobs_table.php │ ├── 2019_01_20_162238_add_title_content_to_blogposts_table.php │ ├── 2019_05_09_155933_create_jobs_table.php │ ├── 2019_05_02_180253_add_polymorph_to_comments_table.php │ ├── 2019_02_27_160516_create_comments_table.php │ ├── 2019_04_22_111215_add_user_to_comments_table.php │ ├── 2019_04_20_154241_create_blog_post_tag_table.php │ ├── 2019_03_20_181629_add_user_to_blog_posts_table.php │ ├── 2019_04_02_162450_add_cascade_delete_to_comments_table.php │ └── 2019_05_05_123450_rename_blog_post_tag_table_to_taggables.php ├── bootstrap ├── cache │ └── .gitignore └── app.php ├── storage ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore ├── debugbar │ └── .gitignore └── framework │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── cache │ ├── data │ │ └── .gitignore │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── .prittierrc.json ├── .gitattributes ├── .vscode └── settings.json ├── app ├── Contracts │ └── CounterContract.php ├── Author.php ├── Profile.php ├── Services │ ├── DummyCounter.php │ └── Counter.php ├── Http │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── CheckForMaintenanceMode.php │ │ ├── TrimStrings.php │ │ ├── TrustProxies.php │ │ ├── Authenticate.php │ │ ├── VerifyCsrfToken.php │ │ ├── RedirectIfAuthenticated.php │ │ └── LocaleMiddleware.php │ ├── Controllers │ │ ├── Controller.php │ │ ├── PostTagController.php │ │ ├── HomeController.php │ │ ├── UserCommentController.php │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── ResetPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── VerificationController.php │ │ │ └── RegisterController.php │ │ ├── PostCommentController.php │ │ ├── Api │ │ │ └── V1 │ │ │ │ └── PostCommentController.php │ │ └── UserController.php │ ├── Resources │ │ ├── CommentUser.php │ │ └── Comment.php │ ├── Requests │ │ ├── StoreComment.php │ │ ├── StorePost.php │ │ └── UpdateUser.php │ ├── ViewComposers │ │ └── ActivityComposer.php │ └── Kernel.php ├── Facades │ └── CounterFacade.php ├── Scopes │ ├── LatestScope.php │ └── DeletedAdminScope.php ├── Image.php ├── Tag.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ ├── RouteServiceProvider.php │ ├── AuthServiceProvider.php │ └── AppServiceProvider.php ├── Observers │ ├── BlogPostObserver.php │ └── CommentObserver.php ├── Mail │ ├── BlogPostAdded.php │ ├── CommentPostedOnPostWatched.php │ ├── CommentPostedMarkdown.php │ └── CommentPosted.php ├── Events │ ├── CommentPosted.php │ └── BlogPostPosted.php ├── Comment.php ├── Listeners │ ├── NotifyAdminWhenBlogPostCreated.php │ ├── CacheSubscriber.php │ └── NotifyUsersAboutComment.php ├── Traits │ └── Taggable.php ├── Console │ └── Kernel.php ├── Jobs │ ├── ThrottledMail.php │ └── NotifyUsersPostWasCommented.php ├── BlogPost.php ├── Exceptions │ └── Handler.php ├── Policies │ ├── UserPolicy.php │ ├── BlogPostPolicy.php │ └── CommentPolicy.php └── User.php ├── .gitignore ├── .editorconfig ├── tests ├── Unit │ └── ExampleTest.php ├── Feature │ ├── ExampleTest.php │ ├── HomeTest.php │ ├── ApiPostCommentsTest.php │ └── PostTest.php ├── CreatesApplication.php └── TestCase.php ├── routes ├── channels.php ├── console.php ├── api.php └── web.php ├── webpack.mix.js ├── server.php ├── .env.example ├── config ├── view.php ├── services.php ├── hashing.php ├── broadcasting.php ├── filesystems.php ├── queue.php ├── logging.php ├── cache.php └── auth.php ├── package.json ├── phpunit.xml ├── artisan ├── composer.json └── readme.md /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lang/de/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lang/es/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/panel.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} 2 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/table.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} 2 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/footer.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} 2 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/promotion.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} 2 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/subcopy.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} 2 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/button.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }}: {{ $url }} -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/header.blade.php: -------------------------------------------------------------------------------- 1 | [{{ $slot }}]({{ $url }}) 2 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/promotion/button.blade.php: -------------------------------------------------------------------------------- 1 | [{{ $slot }}]({{ $url }}) 2 | -------------------------------------------------------------------------------- /.prittierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "semi": true, 4 | "singleQuote": false 5 | } 6 | -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css" 4 | } 5 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/html/table.blade.php: -------------------------------------------------------------------------------- 1 |
This is a secret email secre@laravel.test
6 | @endsection -------------------------------------------------------------------------------- /resources/views/components/badge.blade.php: -------------------------------------------------------------------------------- 1 | @if(!isset($show) || $show) 2 | 3 | {{ $slot }} 4 | 5 | @endif -------------------------------------------------------------------------------- /app/Contracts/CounterContract.php: -------------------------------------------------------------------------------- 1 | 2 |2 | @foreach ($tags as $tag) 3 | {{ $tag->name }} 5 | @endforeach 6 |
-------------------------------------------------------------------------------- /app/Author.php: -------------------------------------------------------------------------------- 1 | hasOne('App\Profile'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/html/subcopy.blade.php: -------------------------------------------------------------------------------- 1 || 4 | {{ Illuminate\Mail\Markdown::parse($slot) }} 5 | | 6 |
| 4 | {{ Illuminate\Mail\Markdown::parse($slot) }} 5 | | 6 |
{{ __('Hello this is contact!') }}
6 | 7 | @can('home.secret') 8 |9 | 10 | Go to special contact details! 11 | 12 |
13 | @endcan 14 | @endsection -------------------------------------------------------------------------------- /app/Services/DummyCounter.php: -------------------------------------------------------------------------------- 1 | 5 | @csrf 6 | 7 | @include('posts._form') 8 | 9 | 10 | 11 | @endsection -------------------------------------------------------------------------------- /resources/views/components/comment-list.blade.php: -------------------------------------------------------------------------------- 1 | @forelse($comments as $comment) 2 |3 | {{ $comment->content }} 4 |
5 | @tags(['tags' => $comment->tags])@endtags 6 | @updated(['date' => $comment->created_at, 'name' => $comment->user->name, 'userId' => $comment->user->id]) 7 | @endupdated 8 | @empty 9 |{{ __('No comments yet!') }}
10 | @endforelse -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | assertTrue(2 == 2); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/Facades/CounterFacade.php: -------------------------------------------------------------------------------- 1 | orderBy($model::CREATED_AT, 'desc'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/html/footer.blade.php: -------------------------------------------------------------------------------- 1 || 6 | {{ Illuminate\Mail\Markdown::parse($slot) }} 7 | | 8 |
2 | {{ empty(trim($slot)) ? __('Added') : $slot }} {{ $date->diffForHumans() }} 3 | @if(isset($name)) 4 | @if(isset($userId)) 5 | {{ __('by') }} {{ $name }} 6 | @else 7 | {{ __('by') }} {{ $name }} 8 | @endif 9 | @endif 10 |
-------------------------------------------------------------------------------- /app/Image.php: -------------------------------------------------------------------------------- 1 | morphTo(); 15 | } 16 | 17 | public function url() 18 | { 19 | return Storage::url($this->path); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /resources/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // Body 3 | $body-bg: #f8fafc; 4 | 5 | // Typography 6 | $font-family-sans-serif: "Nunito", sans-serif; 7 | $font-size-base: 0.9rem; 8 | $line-height-base: 1.6; 9 | 10 | // Colors 11 | $blue: #3490dc; 12 | $indigo: #6574cd; 13 | $purple: #9561e2; 14 | $pink: #f66D9b; 15 | $red: #e3342f; 16 | $orange: #f6993f; 17 | $yellow: #ffed4a; 18 | $green: #38c172; 19 | $teal: #4dc0b5; 20 | $cyan: #6cb2eb; 21 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | $post->id]) }}" 6 | enctype="multipart/form-data"> 7 | @csrf 8 | @method('PUT') 9 | 10 | @include('posts._form') 11 | 12 | 13 | 14 | @endsection -------------------------------------------------------------------------------- /app/Tag.php: -------------------------------------------------------------------------------- 1 | morphedByMany('App\BlogPost', 'taggable')->withTimestamps()->as('tagged'); 12 | } 13 | 14 | public function comments() 15 | { 16 | return $this->morphedByMany('App\Comment', 'taggable')->withTimestamps()->as('tagged'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Controllers/PostTagController.php: -------------------------------------------------------------------------------- 1 | $tag->blogPosts() 16 | ->latestWithRelations() 17 | ->get(), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /resources/sass/app.scss: -------------------------------------------------------------------------------- 1 | 2 | // Fonts 3 | @import url('https://fonts.googleapis.com/css?family=Nunito'); 4 | 5 | // Variables 6 | @import 'variables'; 7 | 8 | // Bootstrap 9 | @import '~bootstrap/scss/bootstrap'; 10 | 11 | .navbar-laravel { 12 | background-color: #fff; 13 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); 14 | } 15 | 16 | .fm-inline { 17 | display: inline; 18 | } 19 | 20 | .badge-lg { 21 | font-size: 1.0rem; 22 | } 23 | 24 | .avatar { 25 | width: 128px; 26 | height: 128px; 27 | } -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | 2 || 7 | {{ Illuminate\Mail\Markdown::parse($slot) }} 8 | | 9 |
4 |
|
12 |
{{ __('messages.example_with_value', ['name' => 'John']) }}
8 | 9 |{{ trans_choice('messages.plural', 0, ['a' => 1]) }}
10 |{{ trans_choice('messages.plural', 1, ['a' => 1]) }}
11 |{{ trans_choice('messages.plural', 2, ['a' => 1]) }}
12 | 13 |Using JSON: {{ __('Welcome to Laravel!') }}
14 |Using JSON: {{ __('Hello :name', ['name' => 'Piotr']) }}
15 | 16 |This is the content of the main page!
17 | @endsection -------------------------------------------------------------------------------- /resources/views/emails/posts/comment-posted-on-watched.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | # Comment was posted on post you're watching 3 | 4 | Hi {{ $user->name }} 5 | 6 | @component('mail::button', ['url' => route('posts.show', ['post' => $comment->commentable->id])]) 7 | View The Blog Post 8 | @endcomponent 9 | 10 | @component('mail::button', ['url' => route('users.show', ['user' => $comment->user->id])]) 11 | Visit {{ $comment->user->name }} profile 12 | @endcomponent 13 | 14 | @component('mail::panel') 15 | {{ $comment->content }} 16 | @endcomponent 17 | 18 | Thanks,Currently viewed by {{ $counter }} other users
13 | 14 | @commentForm(['route' => route('users.comments.store', ['user' => $user->id])]) 15 | @endcommentForm 16 | 17 | @commentList(['comments' => $user->commentsOn]) 18 | @endcommentList 19 |Hi {{ $comment->commentable->user->name }}
8 | 9 |10 | Someone has commented on your blog post 11 | 12 | {{ $comment->commentable->title }} 13 | 14 |
15 | 16 |
19 |
20 |
21 | {{ $comment->user->name }}
22 | said:
23 |
26 | "{{ $comment->content }}" 27 |
-------------------------------------------------------------------------------- /app/Mail/BlogPostAdded.php: -------------------------------------------------------------------------------- 1 | markdown('emails.posts.blog-post-added'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2019_02_25_124726_create_authors_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('authors'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2019_01_20_161408_create_blogposts_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('blogposts'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Http/Resources/Comment.php: -------------------------------------------------------------------------------- 1 | $this->id, 20 | 'content' => $this->content, 21 | 'created_at' => (string)$this->created_at, 22 | 'updated_at' => (string)$this->updated_at, 23 | 'user' => new CommentUserResource($this->whenLoaded('user')) 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Events/CommentPosted.php: -------------------------------------------------------------------------------- 1 | comment = $comment; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Events/BlogPostPosted.php: -------------------------------------------------------------------------------- 1 | blogPost = $blogPost; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/html/message.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::layout') 2 | {{-- Header --}} 3 | @slot('header') 4 | @component('mail::header', ['url' => config('app.url')]) 5 | {{ config('app.name') }} 6 | @endcomponent 7 | @endslot 8 | 9 | {{-- Body --}} 10 | {{ $slot }} 11 | 12 | {{-- Subcopy --}} 13 | @isset($subcopy) 14 | @slot('subcopy') 15 | @component('mail::subcopy') 16 | {{ $subcopy }} 17 | @endcomponent 18 | @endslot 19 | @endisset 20 | 21 | {{-- Footer --}} 22 | @slot('footer') 23 | @component('mail::footer') 24 | © {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.') 25 | @endcomponent 26 | @endslot 27 | @endcomponent 28 | -------------------------------------------------------------------------------- /app/Comment.php: -------------------------------------------------------------------------------- 1 | morphTo(); 21 | } 22 | 23 | public function user() 24 | { 25 | return $this->belongsTo('App\User'); 26 | } 27 | 28 | public function scopeLatest(Builder $query) 29 | { 30 | return $query->orderBy(static::CREATED_AT, 'desc'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Listeners/NotifyAdminWhenBlogPostCreated.php: -------------------------------------------------------------------------------- 1 | get() 23 | ->map(function (User $user) { 24 | ThrottledMail::dispatch( 25 | new BlogPostAdded(), 26 | $user 27 | ); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2019_04_20_154128_create_tags_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 40); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('tags'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/markdown/message.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::layout') 2 | {{-- Header --}} 3 | @slot('header') 4 | @component('mail::header', ['url' => config('app.url')]) 5 | {{ config('app.name') }} 6 | @endcomponent 7 | @endslot 8 | 9 | {{-- Body --}} 10 | {{ $slot }} 11 | 12 | {{-- Subcopy --}} 13 | @isset($subcopy) 14 | @slot('subcopy') 15 | @component('mail::subcopy') 16 | {{ $subcopy }} 17 | @endcomponent 18 | @endslot 19 | @endisset 20 | 21 | {{-- Footer --}} 22 | @slot('footer') 23 | @component('mail::footer') 24 | © {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.') 25 | @endcomponent 26 | @endslot 27 | @endcomponent 28 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email', 191)->index(); 16 | $table->string('token'); 17 | $table->timestamp('created_at')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down() 25 | { 26 | Schema::dropIfExists('password_resets'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Requests/StorePost.php: -------------------------------------------------------------------------------- 1 | 'bail|min:5|required|max:100', 28 | 'content' => 'required|min:10', 29 | 'thumbnail' => 'image|mimes:jpg,jpeg,png,gif,svg|max:1024|dimensions:min_height=500' 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | LOG_CHANNEL=stack 8 | 9 | DB_CONNECTION=mysql 10 | DB_HOST=127.0.0.1 11 | DB_PORT=3306 12 | DB_DATABASE=homestead 13 | DB_USERNAME=homestead 14 | DB_PASSWORD=secret 15 | 16 | BROADCAST_DRIVER=log 17 | CACHE_DRIVER=file 18 | QUEUE_CONNECTION=sync 19 | SESSION_DRIVER=file 20 | SESSION_LIFETIME=120 21 | 22 | REDIS_HOST=127.0.0.1 23 | REDIS_PASSWORD=null 24 | REDIS_PORT=6379 25 | 26 | MAIL_DRIVER=smtp 27 | MAIL_HOST=smtp.mailtrap.io 28 | MAIL_PORT=2525 29 | MAIL_USERNAME=null 30 | MAIL_PASSWORD=null 31 | MAIL_ENCRYPTION=null 32 | 33 | PUSHER_APP_ID= 34 | PUSHER_APP_KEY= 35 | PUSHER_APP_SECRET= 36 | PUSHER_APP_CLUSTER=mt1 37 | 38 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 39 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 40 | -------------------------------------------------------------------------------- /app/Traits/Taggable.php: -------------------------------------------------------------------------------- 1 | tags()->sync(static::findTagsInContent($model->content)); 12 | }); 13 | 14 | static::created(function ($model) { 15 | $model->tags()->sync(static::findTagsInContent($model->content)); 16 | }); 17 | } 18 | 19 | public function tags() 20 | { 21 | return $this->morphToMany('App\Tag', 'taggable')->withTimestamps(); 22 | } 23 | 24 | private static function findTagsInContent($content) 25 | { 26 | preg_match_all('/@([^@]+)@/m', $content, $tags); 27 | 28 | return Tag::whereIn('name', $tags[1] ?? [])->get(); 29 | } 30 | } -------------------------------------------------------------------------------- /database/migrations/2019_04_04_162648_add_soft_deletes_to_comments_table.php: -------------------------------------------------------------------------------- 1 | softDeletes(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('comments', function (Blueprint $table) { 29 | $table->dropSoftDeletes(); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2019_05_14_141217_add_locale_to_users_table.php: -------------------------------------------------------------------------------- 1 | string('locale', 3)->default('en'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('users', function (Blueprint $table) { 29 | $table->dropColumn('locale'); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2019_04_02_163816_add_soft_deletes_to_blog_posts_table.php: -------------------------------------------------------------------------------- 1 | softDeletes(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('blog_posts', function (Blueprint $table) { 29 | $table->dropSoftDeletes(); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2019_04_06_145253_add_is_admin_to_users_table.php: -------------------------------------------------------------------------------- 1 | boolean('is_admin')->default(false); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('users', function (Blueprint $table) { 29 | $table->dropColumn('is_admin'); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/views/vendor/mail/html/button.blade.php: -------------------------------------------------------------------------------- 1 |
4 |
|
18 |
{{ $post->content }}
24 | 25 | @updated(['date' => $post->created_at, 'name' => $post->user->name]) 26 | @endupdated 27 | @updated(['date' => $post->updated_at]) 28 | {{ __('Updated') }} 29 | @endupdated 30 | 31 | @tags(['tags' => $post->tags])@endtags 32 | 33 |{{ trans_choice('messages.people.reading', $counter) }}
34 | 35 |
29 |
|
51 |
8 |
24 | {{ trans_choice('messages.comments', $post->comments_count) }} 25 |
26 | 27 | @auth 28 | @can('update', $post) 29 | 31 | {{ __('Edit') }} 32 | 33 | @endcan 34 | @endauth 35 | 36 | {{-- @cannot('delete', $post) 37 |You can't delete this post
38 | @endcannot --}} 39 | 40 | @auth 41 | @if(!$post->trashed()) 42 | @can('delete', $post) 43 | 50 | @endcan 51 | @endif 52 | @endauth 53 | 54 | @empty 55 |{{ __('No blog posts yet!') }}
56 | @endforelse 57 |49 | {{ session()->get('status') }} 50 |
51 | @endif 52 | 53 | @yield('content') 54 |8 | High-quality, comprehensive courses for web developers. 9 |
10 | 11 |12 | About the Instructor · 13 | Courses · 14 | Contact & Links · 15 | This Course Resources 16 |
17 |48 | Explore, Learn, and Grow with My Comprehensive Web Development Courses! 49 |
50 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/V1/PostCommentController.php: -------------------------------------------------------------------------------- 1 | middleware('auth:api')->only(['store', 'update', 'destroy']); 18 | } 19 | 20 | /** 21 | * Display a listing of the resource. 22 | * 23 | * @return \Illuminate\Http\Response 24 | */ 25 | public function index(BlogPost $post, Request $request) 26 | { 27 | $perPage = $request->input('per_page') ?? 15; 28 | return CommentResource::collection( 29 | $post->comments()->with('user')->paginate($perPage)->appends( 30 | [ 31 | 'per_page' => $perPage 32 | ] 33 | ) 34 | ); 35 | } 36 | 37 | /** 38 | * Store a newly created resource in storage. 39 | * 40 | * @param \Illuminate\Http\Request $request 41 | * @return \Illuminate\Http\Response 42 | */ 43 | public function store(BlogPost $post, StoreComment $request) 44 | { 45 | $comment = $post->comments()->create([ 46 | 'content' => $request->input('content'), 47 | 'user_id' => $request->user()->id 48 | ]); 49 | event(new CommentPosted($comment)); 50 | 51 | return new CommentResource($comment); 52 | } 53 | 54 | /** 55 | * Display the specified resource. 56 | * 57 | * @param int $id 58 | * @return \Illuminate\Http\Response 59 | */ 60 | public function show(BlogPost $post, Comment $comment) 61 | { 62 | return new CommentResource($comment); 63 | } 64 | 65 | /** 66 | * Update the specified resource in storage. 67 | * 68 | * @param \Illuminate\Http\Request $request 69 | * @param int $id 70 | * @return \Illuminate\Http\Response 71 | */ 72 | public function update(BlogPost $post, Comment $comment, StoreComment $request) 73 | { 74 | $this->authorize($comment); 75 | $comment->content = $request->input('content'); 76 | $comment->save(); 77 | 78 | return new CommentResource($comment); 79 | } 80 | 81 | /** 82 | * Remove the specified resource from storage. 83 | * 84 | * @param int $id 85 | * @return \Illuminate\Http\Response 86 | */ 87 | public function destroy(BlogPost $post, Comment $comment) 88 | { 89 | $this->authorize($comment); 90 | $comment->delete(); 91 | 92 | return response()->noContent(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'sync'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection information for each server that 24 | | is used by your application. A default configuration has been added 25 | | for each back-end shipped with Laravel. You are free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'table' => 'jobs', 40 | 'queue' => 'default', 41 | 'retry_after' => 90, 42 | ], 43 | 44 | 'beanstalkd' => [ 45 | 'driver' => 'beanstalkd', 46 | 'host' => 'localhost', 47 | 'queue' => 'default', 48 | 'retry_after' => 90, 49 | ], 50 | 51 | 'sqs' => [ 52 | 'driver' => 'sqs', 53 | 'key' => env('SQS_KEY', 'your-public-key'), 54 | 'secret' => env('SQS_SECRET', 'your-secret-key'), 55 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 56 | 'queue' => env('SQS_QUEUE', 'your-queue-name'), 57 | 'region' => env('SQS_REGION', 'us-east-1'), 58 | ], 59 | 60 | 'redis' => [ 61 | 'driver' => 'redis', 62 | 'connection' => 'default', 63 | 'queue' => env('REDIS_QUEUE', 'default'), 64 | 'retry_after' => 90, 65 | 'block_for' => null, 66 | ], 67 | 68 | ], 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Failed Queue Jobs 73 | |-------------------------------------------------------------------------- 74 | | 75 | | These options configure the behavior of failed queue job logging so you 76 | | can control which database and table are used to store the jobs that 77 | | have failed. You may change them to any database / table you wish. 78 | | 79 | */ 80 | 81 | 'failed' => [ 82 | 'database' => env('DB_CONNECTION', 'mysql'), 83 | 'table' => 'failed_jobs', 84 | ], 85 | 86 | ]; 87 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Log Channels 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may configure the log channels for your application. Out of 27 | | the box, Laravel uses the Monolog PHP logging library. This gives 28 | | you a variety of powerful log handlers / formatters to utilize. 29 | | 30 | | Available Drivers: "single", "daily", "slack", "syslog", 31 | | "errorlog", "monolog", 32 | | "custom", "stack" 33 | | 34 | */ 35 | 36 | 'channels' => [ 37 | 'stack' => [ 38 | 'driver' => 'stack', 39 | 'channels' => ['daily'], 40 | ], 41 | 42 | 'single' => [ 43 | 'driver' => 'single', 44 | 'path' => storage_path('logs/laravel.log'), 45 | 'level' => 'debug', 46 | ], 47 | 48 | 'daily' => [ 49 | 'driver' => 'daily', 50 | 'path' => storage_path('logs/laravel.log'), 51 | 'level' => 'debug', 52 | 'days' => 14, 53 | ], 54 | 55 | 'slack' => [ 56 | 'driver' => 'slack', 57 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 58 | 'username' => 'Laravel Log', 59 | 'emoji' => ':boom:', 60 | 'level' => 'critical', 61 | ], 62 | 63 | 'papertrail' => [ 64 | 'driver' => 'monolog', 65 | 'level' => 'debug', 66 | 'handler' => SyslogUdpHandler::class, 67 | 'handler_with' => [ 68 | 'host' => env('PAPERTRAIL_URL'), 69 | 'port' => env('PAPERTRAIL_PORT'), 70 | ], 71 | ], 72 | 73 | 'stderr' => [ 74 | 'driver' => 'monolog', 75 | 'handler' => StreamHandler::class, 76 | 'formatter' => env('LOG_STDERR_FORMATTER'), 77 | 'with' => [ 78 | 'stream' => 'php://stderr', 79 | ], 80 | ], 81 | 82 | 'syslog' => [ 83 | 'driver' => 'syslog', 84 | 'level' => 'debug', 85 | ], 86 | 87 | 'errorlog' => [ 88 | 'driver' => 'errorlog', 89 | 'level' => 'debug', 90 | ], 91 | ], 92 | 93 | ]; 94 | -------------------------------------------------------------------------------- /tests/Feature/ApiPostCommentsTest.php: -------------------------------------------------------------------------------- 1 | blogPost(); 18 | 19 | $response = $this->json('GET', 'api/v1/posts/1/comments'); 20 | 21 | $response->assertStatus(200) 22 | ->assertJsonStructure(['data', 'links', 'meta']) 23 | ->assertJsonCount(0, 'data'); 24 | } 25 | 26 | public function testBlogPostHas10Comments() 27 | { 28 | $this->blogPost()->each(function (BlogPost $post) { 29 | $post->comments()->saveMany( 30 | factory(Comment::class, 10)->make([ 31 | 'user_id' => $this->user()->id 32 | ]) 33 | ); 34 | }); 35 | 36 | $response = $this->json('GET', 'api/v1/posts/2/comments'); 37 | 38 | $response->assertStatus(200) 39 | ->assertJsonStructure( 40 | [ 41 | 'data' => [ 42 | '*' => [ 43 | 'id', 44 | 'content', 45 | 'created_at', 46 | 'updated_at', 47 | 'user' => [ 48 | 'id', 49 | 'name' 50 | ] 51 | ] 52 | ], 53 | 'links', 54 | 'meta' 55 | ] 56 | ) 57 | ->assertJsonCount(10, 'data'); 58 | } 59 | 60 | public function testAddingCommentsWhenNotAuthenticated() 61 | { 62 | $this->blogPost(); 63 | 64 | $response = $this->json('POST', 'api/v1/posts/3/comments', [ 65 | 'content' => 'Hello' 66 | ]); 67 | 68 | $response->assertStatus(401); 69 | } 70 | 71 | public function testAddingCommentsWhenAuthenicated() 72 | { 73 | $this->blogPost(); 74 | 75 | $response = $this->actingAs($this->user(), 'api')->json('POST', 'api/v1/posts/4/comments', [ 76 | 'content' => 'Hello' 77 | ]); 78 | 79 | $response->assertStatus(201); 80 | } 81 | 82 | public function testAddingCommentWithInvalidData() 83 | { 84 | $this->blogPost(); 85 | 86 | $response = $this->actingAs($this->user(), 'api')->json('POST', 'api/v1/posts/5/comments', []); 87 | 88 | $response->assertStatus(422) 89 | ->assertJson([ 90 | "message" => "The given data was invalid.", 91 | "errors" => [ 92 | "content" => [ 93 | "The content field is required." 94 | ] 95 | ] 96 | ]); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_DRIVER', 'file'), 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Cache Stores 25 | |-------------------------------------------------------------------------- 26 | | 27 | | Here you may define all of the cache "stores" for your application as 28 | | well as their drivers. You may even define multiple stores for the 29 | | same cache driver to group types of items stored in your caches. 30 | | 31 | */ 32 | 33 | 'stores' => [ 34 | 35 | 'apc' => [ 36 | 'driver' => 'apc', 37 | ], 38 | 39 | 'array' => [ 40 | 'driver' => 'array', 41 | ], 42 | 43 | 'database' => [ 44 | 'driver' => 'database', 45 | 'table' => 'cache', 46 | 'connection' => null, 47 | ], 48 | 49 | 'file' => [ 50 | 'driver' => 'file', 51 | 'path' => storage_path('framework/cache/data'), 52 | ], 53 | 54 | 'memcached' => [ 55 | 'driver' => 'memcached', 56 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 57 | 'sasl' => [ 58 | env('MEMCACHED_USERNAME'), 59 | env('MEMCACHED_PASSWORD'), 60 | ], 61 | 'options' => [ 62 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 63 | ], 64 | 'servers' => [ 65 | [ 66 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 67 | 'port' => env('MEMCACHED_PORT', 11211), 68 | 'weight' => 100, 69 | ], 70 | ], 71 | ], 72 | 73 | 'redis' => [ 74 | 'driver' => 'redis', 75 | 'connection' => 'cache', 76 | ], 77 | 78 | ], 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Cache Key Prefix 83 | |-------------------------------------------------------------------------- 84 | | 85 | | When utilizing a RAM based store such as APC or Memcached, there might 86 | | be other applications utilizing the same cache. So, we'll specify a 87 | | value to get prefixed to all our keys so we can avoid collisions. 88 | | 89 | */ 90 | 91 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'), 92 | 93 | ]; 94 | -------------------------------------------------------------------------------- /app/Http/Controllers/UserController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 16 | $this->authorizeResource(User::class, 'user'); 17 | } 18 | 19 | /** 20 | * Display a listing of the resource. 21 | * 22 | * @return \Illuminate\Http\Response 23 | */ 24 | public function index() 25 | { 26 | // 27 | } 28 | 29 | /** 30 | * Show the form for creating a new resource. 31 | * 32 | * @return \Illuminate\Http\Response 33 | */ 34 | public function create() 35 | { 36 | // 37 | } 38 | 39 | /** 40 | * Store a newly created resource in storage. 41 | * 42 | * @param \Illuminate\Http\Request $request 43 | * @return \Illuminate\Http\Response 44 | */ 45 | public function store(Request $request) 46 | { 47 | // 48 | } 49 | 50 | /** 51 | * Display the specified resource. 52 | * 53 | * @param \App\User $user 54 | * @return \Illuminate\Http\Response 55 | */ 56 | public function show(User $user) 57 | { 58 | return view('users.show', [ 59 | 'user' => $user, 60 | 'counter' => CounterFacade::increment("user-{$user->id}") 61 | ]); 62 | } 63 | 64 | /** 65 | * Show the form for editing the specified resource. 66 | * 67 | * @param \App\User $user 68 | * @return \Illuminate\Http\Response 69 | */ 70 | public function edit(User $user) 71 | { 72 | return view('users.edit', ['user' => $user]); 73 | } 74 | 75 | /** 76 | * Update the specified resource in storage. 77 | * 78 | * @param \Illuminate\Http\Request $request 79 | * @param \App\User $user 80 | * @return \Illuminate\Http\Response 81 | */ 82 | public function update(UpdateUser $request, User $user) 83 | { 84 | if ($request->hasFile('avatar')) { 85 | $path = $request->file('avatar')->store('avatars'); 86 | 87 | if ($user->image) { 88 | $user->image->path = $path; 89 | $user->image->save(); 90 | } else { 91 | $user->image()->save( 92 | Image::make(['path' => $path]) 93 | ); 94 | } 95 | } 96 | 97 | $user->locale = $request->get('locale'); 98 | $user->save(); 99 | 100 | return redirect() 101 | ->back() 102 | ->withStatus('Profile was updated!'); 103 | } 104 | 105 | /** 106 | * Remove the specified resource from storage. 107 | * 108 | * @param \App\User $user 109 | * @return \Illuminate\Http\Response 110 | */ 111 | public function destroy(User $user) 112 | { 113 | // 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | [ 31 | \App\Http\Middleware\EncryptCookies::class, 32 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 33 | \Illuminate\Session\Middleware\StartSession::class, 34 | // \Illuminate\Session\Middleware\AuthenticateSession::class, 35 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 36 | \App\Http\Middleware\VerifyCsrfToken::class, 37 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 38 | \App\Http\Middleware\LocaleMiddleware::class 39 | ], 40 | 41 | 'api' => [ 42 | 'throttle:60,1', 43 | 'bindings', 44 | ], 45 | ]; 46 | 47 | /** 48 | * The application's route middleware. 49 | * 50 | * These middleware may be assigned to groups or used individually. 51 | * 52 | * @var array 53 | */ 54 | protected $routeMiddleware = [ 55 | 'auth' => \App\Http\Middleware\Authenticate::class, 56 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 57 | 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 58 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 59 | 'can' => \Illuminate\Auth\Middleware\Authorize::class, 60 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 61 | 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 62 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 63 | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 64 | 'locale' => \App\Http\Middleware\LocaleMiddleware::class 65 | ]; 66 | 67 | /** 68 | * The priority-sorted list of middleware. 69 | * 70 | * This forces non-global middleware to always be in the given order. 71 | * 72 | * @var array 73 | */ 74 | protected $middlewarePriority = [ 75 | \Illuminate\Session\Middleware\StartSession::class, 76 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 77 | \App\Http\Middleware\Authenticate::class, 78 | \Illuminate\Session\Middleware\AuthenticateSession::class, 79 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 80 | \Illuminate\Auth\Middleware\Authorize::class, 81 | ]; 82 | } 83 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => 'users', 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session", "token" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | 44 | 'api' => [ 45 | 'driver' => 'token', 46 | 'provider' => 'users', 47 | // 'input_key' => 'api_token', 48 | // 'storage_key' => 'api_token' 49 | ], 50 | ], 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | User Providers 55 | |-------------------------------------------------------------------------- 56 | | 57 | | All authentication drivers have a user provider. This defines how the 58 | | users are actually retrieved out of your database or other storage 59 | | mechanisms used by this application to persist your user's data. 60 | | 61 | | If you have multiple user tables or models you may configure multiple 62 | | sources which represent each model / table. These sources may then 63 | | be assigned to any extra authentication guards you have defined. 64 | | 65 | | Supported: "database", "eloquent" 66 | | 67 | */ 68 | 69 | 'providers' => [ 70 | 'users' => [ 71 | 'driver' => 'eloquent', 72 | 'model' => App\User::class, 73 | ], 74 | 75 | // 'users' => [ 76 | // 'driver' => 'database', 77 | // 'table' => 'users', 78 | // ], 79 | ], 80 | 81 | /* 82 | |-------------------------------------------------------------------------- 83 | | Resetting Passwords 84 | |-------------------------------------------------------------------------- 85 | | 86 | | You may specify multiple password reset configurations if you have more 87 | | than one user table or model in the application and you want to have 88 | | separate password reset settings based on the specific user types. 89 | | 90 | | The expire time is the number of minutes that the reset token should be 91 | | considered valid. This security feature keeps tokens short-lived so 92 | | they have less time to be guessed. You may change this as needed. 93 | | 94 | */ 95 | 96 | 'passwords' => [ 97 | 'users' => [ 98 | 'provider' => 'users', 99 | 'table' => 'password_resets', 100 | 'expire' => 60, 101 | ], 102 | ], 103 | 104 | ]; 105 | -------------------------------------------------------------------------------- /public/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Feature/PostTest.php: -------------------------------------------------------------------------------- 1 | get('/posts'); 17 | $response->assertSeeText('No blog posts yet!'); 18 | } 19 | 20 | public function testSee1BlogPostWhenThereIs1WithNoComments() 21 | { 22 | // Arrange 23 | $post = $this->createDummyBlogPost(); 24 | 25 | // Act 26 | $response = $this->get('/posts'); 27 | 28 | // Assert 29 | $response->assertSeeText('New title'); 30 | $response->assertSeeText('No comments yet'); 31 | 32 | $this->assertDatabaseHas('blog_posts', [ 33 | 'title' => 'New title', 34 | ]); 35 | } 36 | 37 | public function testSee1BlogPostWithComments() 38 | { 39 | // Arrange 40 | $user = $this->user(); 41 | 42 | $post = $this->createDummyBlogPost(); 43 | factory(Comment::class, 4)->create([ 44 | 'commentable_id' => $post->id, 45 | 'commentable_type' => 'App\BlogPost', 46 | 'user_id' => $user->id 47 | ]); 48 | 49 | $response = $this->get('/posts'); 50 | 51 | $response->assertSeeText('4 comments'); 52 | } 53 | 54 | public function testStoreValid() 55 | { 56 | $params = [ 57 | 'title' => 'Valid title', 58 | 'content' => 'At least 10 characters', 59 | ]; 60 | 61 | $this->actingAs($this->user()) 62 | ->post('/posts', $params) 63 | ->assertStatus(302) 64 | ->assertSessionHas('status'); 65 | 66 | $this->assertEquals(session('status'), 'Blog post was created!'); 67 | } 68 | 69 | public function testStoreFail() 70 | { 71 | $params = [ 72 | 'title' => 'x', 73 | 'content' => 'x', 74 | ]; 75 | 76 | $this->actingAs($this->user()) 77 | ->post('/posts', $params) 78 | ->assertStatus(302) 79 | ->assertSessionHas('errors'); 80 | 81 | $messages = session('errors')->getMessages(); 82 | 83 | $this->assertEquals($messages['title'][0], 'The title must be at least 5 characters.'); 84 | $this->assertEquals($messages['content'][0], 'The content must be at least 10 characters.'); 85 | } 86 | 87 | public function testUpdateValid() 88 | { 89 | $user = $this->user(); 90 | $post = $this->createDummyBlogPost($user->id); 91 | 92 | $this->assertDatabaseHas('blog_posts', $post->toArray()); 93 | 94 | $params = [ 95 | 'title' => 'A new named title', 96 | 'content' => 'Content was changed', 97 | ]; 98 | 99 | $this->actingAs($user) 100 | ->put("/posts/{$post->id}", $params) 101 | ->assertStatus(302) 102 | ->assertSessionHas('status'); 103 | 104 | $this->assertEquals(session('status'), 'Blog post was updated!'); 105 | $this->assertDatabaseMissing('blog_posts', $post->toArray()); 106 | $this->assertDatabaseHas('blog_posts', [ 107 | 'title' => 'A new named title', 108 | ]); 109 | } 110 | 111 | public function testDelete() 112 | { 113 | $user = $this->user(); 114 | $post = $this->createDummyBlogPost($user->id); 115 | $this->assertDatabaseHas('blog_posts', $post->toArray()); 116 | 117 | $this->actingAs($user) 118 | ->delete("/posts/{$post->id}") 119 | ->assertStatus(302) 120 | ->assertSessionHas('status'); 121 | 122 | $this->assertEquals(session('status'), 'Blog post was deleted!'); 123 | // $this->assertDatabaseMissing('blog_posts', $post->toArray()); 124 | $this->assertSoftDeleted('blog_posts', $post->toArray()); 125 | } 126 | 127 | private function createDummyBlogPost($userId = null): BlogPost 128 | { 129 | // $post = new BlogPost(); 130 | // $post->title = 'New title'; 131 | // $post->content = 'Content of the blog post'; 132 | // $post->save(); 133 | 134 | return factory(BlogPost::class)->states('new-title')->create( 135 | [ 136 | 'user_id' => $userId ?? $this->user()->id, 137 | ] 138 | ); 139 | 140 | // return $post; 141 | } 142 | } 143 | --------------------------------------------------------------------------------