From aaa483289acf71fda059e5cf4685a078bbc18366 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sat, 4 Sep 2021 10:35:20 +0200 Subject: [PATCH 1/5] Install views plugin --- composer.json | 1 + composer.lock | 131 +++++++++++++++++- config/eloquent-viewable.php | 107 ++++++++++++++ .../2021_09_04_082939_create_views_table.php | 62 +++++++++ 4 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 config/eloquent-viewable.php create mode 100644 database/migrations/2021_09_04_082939_create_views_table.php diff --git a/composer.json b/composer.json index 4636ebb..92ae40c 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "license": "MIT", "require": { "php": "^7.3|^8.0", + "cyrildewit/eloquent-viewable": "^6.0", "doctrine/dbal": "^3.1", "fruitcake/laravel-cors": "^2.0", "guzzlehttp/guzzle": "^7.0.1", diff --git a/composer.lock b/composer.lock index 293c830..d0cfacc 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": "2e27ad4aeb64c5f5afd5be0288a4a9e2", + "content-hash": "5ceb5b6e26fc35fd4fd26e6d152d3b11", "packages": [ { "name": "asm89/stack-cors", @@ -195,6 +195,83 @@ ], "time": "2021-08-17T13:49:14+00:00" }, + { + "name": "cyrildewit/eloquent-viewable", + "version": "v6.0.2", + "source": { + "type": "git", + "url": "https://github.com/cyrildewit/eloquent-viewable.git", + "reference": "699294489e397c18a59ff9fafadde8154be85c02" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cyrildewit/eloquent-viewable/zipball/699294489e397c18a59ff9fafadde8154be85c02", + "reference": "699294489e397c18a59ff9fafadde8154be85c02", + "shasum": "" + }, + "require": { + "illuminate/cache": "^6.0|^7.0|^8.0", + "illuminate/contracts": "^6.0|^7.0|^8.0", + "illuminate/cookie": "^6.0|^7.0|^8.0", + "illuminate/database": "^6.0|^7.0|^8.0", + "illuminate/http": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "jaybizzle/crawler-detect": "^1.0", + "nesbot/carbon": "^2.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "illuminate/config": "^6.0|^7.0|^8.0", + "mockery/mockery": "^1.2.4", + "orchestra/testbench": "^4.9.1|^5.9.1|^6.6.1", + "phpunit/phpunit": "^9.3.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "CyrildeWit\\EloquentViewable\\EloquentViewableServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "CyrildeWit\\EloquentViewable\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Cyril de Wit", + "email": "info@cyrildewit.nl", + "homepage": "http://cyrildewit.nl", + "role": "Developer" + } + ], + "description": "Laravel package that allows you to associate views with Eloquent models", + "homepage": "https://github.com/cyrildewit/eloquent-viewable", + "keywords": [ + "counter", + "eloquent", + "hits", + "laravel", + "viewable", + "views", + "visitable", + "visits" + ], + "support": { + "issues": "https://github.com/cyrildewit/eloquent-viewable/issues", + "source": "https://github.com/cyrildewit/eloquent-viewable/tree/v6.0.2" + }, + "time": "2021-01-02T15:47:17+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.1", @@ -1295,6 +1372,58 @@ }, "time": "2021-06-30T20:03:07+00:00" }, + { + "name": "jaybizzle/crawler-detect", + "version": "v1.2.106", + "source": { + "type": "git", + "url": "https://github.com/JayBizzle/Crawler-Detect.git", + "reference": "78bf6792cbf9c569dc0bf2465481978fd2ed0de9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/78bf6792cbf9c569dc0bf2465481978fd2ed0de9", + "reference": "78bf6792cbf9c569dc0bf2465481978fd2ed0de9", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jaybizzle\\CrawlerDetect\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Beech", + "email": "m@rkbee.ch", + "role": "Developer" + } + ], + "description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent", + "homepage": "https://github.com/JayBizzle/Crawler-Detect/", + "keywords": [ + "crawler", + "crawler detect", + "crawler detector", + "crawlerdetect", + "php crawler detect" + ], + "support": { + "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.106" + }, + "time": "2021-05-24T20:30:32+00:00" + }, { "name": "laravel/framework", "version": "v8.58.0", diff --git a/config/eloquent-viewable.php b/config/eloquent-viewable.php new file mode 100644 index 0000000..a3536ed --- /dev/null +++ b/config/eloquent-viewable.php @@ -0,0 +1,107 @@ + [ + + /* + * Here you can configure the default `View` model. + */ + 'view' => [ + + 'table_name' => 'views', + 'connection' => env('DB_CONNECTION', 'mysql'), + + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Configuration + |-------------------------------------------------------------------------- + */ + 'cache' => [ + + /* + * Everthing will be stored under the following key. + */ + 'key' => 'cyrildewit.eloquent-viewable.cache', + + /* + * Here you may define the cache store that should be used. + */ + 'store' => env('CACHE_DRIVER', 'file'), + + ], + + /* + |-------------------------------------------------------------------------- + | Cooldown Configuration + |-------------------------------------------------------------------------- + */ + 'cooldown' => [ + + /* + * Everthing will be stored under the following key in the session. + */ + 'key' => 'cyrildewit.eloquent-viewable.cooldowns', + + ], + + /* + |-------------------------------------------------------------------------- + | Ignore Bots + |-------------------------------------------------------------------------- + | + | If you want to ignore bots, you can specify that here. The default + | service that determines if a visitor is a crawler is a package + | by JayBizzle called CrawlerDetect. + | + */ + 'ignore_bots' => true, + + /* + |-------------------------------------------------------------------------- + | Do Not Track Header + |-------------------------------------------------------------------------- + | + | If you want to honor the DNT header, you can specify that here. We won't + | record views from visitors with the Do Not Track header. + | + */ + 'honor_dnt' => true, + + /* + |-------------------------------------------------------------------------- + | Cookies + |-------------------------------------------------------------------------- + | + | This package binds visitors to views using a cookie. If you want to + | give this cookie a custom name, you can specify that here. + | + */ + + 'visitor_cookie_key' => 'eloquent_viewable', + + /* + |-------------------------------------------------------------------------- + | Ignore IP Addresses + |-------------------------------------------------------------------------- + | + | Ignore views of the following IP addresses. + | + */ + + 'ignored_ip_addresses' => [ + + // '127.0.0.1', + + ], + +]; diff --git a/database/migrations/2021_09_04_082939_create_views_table.php b/database/migrations/2021_09_04_082939_create_views_table.php new file mode 100644 index 0000000..37431ff --- /dev/null +++ b/database/migrations/2021_09_04_082939_create_views_table.php @@ -0,0 +1,62 @@ +schema = Schema::connection( + config('eloquent-viewable.models.view.connection') + ); + + $this->table = config('eloquent-viewable.models.view.table_name'); + } + + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + $this->schema->create($this->table, function (Blueprint $table) { + $table->bigIncrements('id'); + $table->morphs('viewable'); + $table->text('visitor')->nullable(); + $table->string('collection')->nullable(); + $table->timestamp('viewed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + $this->schema->dropIfExists($this->table); + } +} From 13eb2737726e25fd65142d9597fd7e060c64bc6b Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sat, 4 Sep 2021 12:09:14 +0200 Subject: [PATCH 2/5] Remove old views system for blog articles --- .../Controllers/BlogArticleController.php | 2 +- ...084238_remove_views_from_blog_articles.php | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2021_09_04_084238_remove_views_from_blog_articles.php diff --git a/app/Http/Controllers/BlogArticleController.php b/app/Http/Controllers/BlogArticleController.php index 8ede612..f425b85 100644 --- a/app/Http/Controllers/BlogArticleController.php +++ b/app/Http/Controllers/BlogArticleController.php @@ -61,7 +61,7 @@ class BlogArticleController extends Controller public function popular() { $data = BlogArticle::select() - ->orderBy('views', 'desc') + ->orderByUniqueViews('desc') ->orderBy('date', 'desc') ->where('published', true) ->limit(5) diff --git a/database/migrations/2021_09_04_084238_remove_views_from_blog_articles.php b/database/migrations/2021_09_04_084238_remove_views_from_blog_articles.php new file mode 100644 index 0000000..cda1674 --- /dev/null +++ b/database/migrations/2021_09_04_084238_remove_views_from_blog_articles.php @@ -0,0 +1,32 @@ +dropColumn('views'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('blog_articles', function (Blueprint $table) { + $table->integer('views')->default(0); + }); + } +} From 5c260b98f668f987401e9588280232041b88850b Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sat, 4 Sep 2021 12:09:42 +0200 Subject: [PATCH 3/5] Implement new view count system on blog article --- app/Http/Controllers/BlogArticleController.php | 17 +++++++++++++---- app/Models/BlogArticle.php | 9 ++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/BlogArticleController.php b/app/Http/Controllers/BlogArticleController.php index f425b85..97cd7eb 100644 --- a/app/Http/Controllers/BlogArticleController.php +++ b/app/Http/Controllers/BlogArticleController.php @@ -89,14 +89,23 @@ class BlogArticleController extends Controller * @param \App\Models\BlogArticle $blogArticle * @return \Illuminate\Http\JsonResponse */ - public function show(BlogArticle $blogArticle) + public function show(int $id) { - if (!$this->authService->isAuthenticated() && !$blogArticle->published) { + $blogArticle = BlogArticle::withCount('views')->find($id); + + if ($blogArticle === null) { abort(404); } - $blogArticle->views += 1; - $blogArticle->save(); + if (!$this->authService->isAuthenticated()) { + if (!$blogArticle->published) { + abort(404); + } + + views($blogArticle) + ->cooldown(now()->addHour()) + ->record(); + } return response()->json($blogArticle); } diff --git a/app/Models/BlogArticle.php b/app/Models/BlogArticle.php index c7a8acd..c1330a9 100644 --- a/app/Models/BlogArticle.php +++ b/app/Models/BlogArticle.php @@ -2,12 +2,15 @@ namespace App\Models; +use CyrildeWit\EloquentViewable\Contracts\Viewable; +use CyrildeWit\EloquentViewable\InteractsWithViews; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class BlogArticle extends Model +class BlogArticle extends Model implements Viewable { use HasFactory; + use InteractsWithViews; protected $fillable = [ 'title', @@ -15,7 +18,6 @@ class BlogArticle extends Model 'date', 'summary', 'content', - 'views', 'published' ]; @@ -24,7 +26,8 @@ class BlogArticle extends Model 'created_at' => 'datetime', 'updated_at' => 'datetime', 'date' => 'datetime', - 'views' => 'integer', 'published' => 'boolean', ]; + + protected $removeViewsOnDelete = true; } From bdc71ded136ef192c05070bdcf151e2612e6777f Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sat, 4 Sep 2021 12:10:12 +0200 Subject: [PATCH 4/5] Ensure api is stateful for requests from Vue.js --- app/Http/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 28111c8..7988ebe 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -40,7 +40,7 @@ class Kernel extends HttpKernel ], 'api' => [ - // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], From a03989f0e2ed13b45028ca702ec2db7ced7418df Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sat, 4 Sep 2021 12:35:16 +0200 Subject: [PATCH 5/5] Add note to fix sanctum when using non-standard port --- .env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.env.example b/.env.example index 44853cd..c8db88a 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,8 @@ FILESYSTEM_DRIVER=local QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 +# Only required if using a custom port on APP_URL +# SANCTUM_STATEFUL_DOMAINS=localhost:8000 MEMCACHED_HOST=127.0.0.1