This commit is contained in:
Shaun Collins
2026-03-04 16:34:33 +00:00
committed by shaun collins
commit 646041230b
15 changed files with 459 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
class GenerateSitemap extends Command
{
protected $signature = 'sitemap:generate';
protected $description = 'Generate the sitemap dynamically using config routes';
private const PRIORITY = 0.5;
private const CHANGE_FREQUENCY = 'weekly';
public function handle()
{
$sitemap = Sitemap::create();
$routes = config('routes.web', []);
foreach ($routes as $route) {
if (isset($route['path'])) {
$this->info('Adding '.$route['path'].' to sitemap.');
$sitemap->add(Url::create($route['path'])
->setPriority($route['priority'] ?? static::PRIORITY)
->setLastModificationDate($route['last_modified'] ?? now())
->setChangeFrequency($route['frequency'] ?? static::CHANGE_FREQUENCY));
}
}
$sitemap->writeToFile(public_path('sitemap.xml'));
$this->info('Sitemap has been successfully generated!');
}
}
@@ -0,0 +1,78 @@
<?php
namespace App\Http\Controllers;
use App\Models\Contact;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class ContactController extends Controller
{
private const SUBJECT = 'New Contact us page message';
public function email(Request $request): RedirectResponse
{
$request->validate([
'name' => 'required',
'email' => 'required|email',
'message' => 'required',
]);
$this->createRecord([
'name' => $request->input('name'),
'email' => $request->input('email'),
'subject' => self::SUBJECT,
'message' => $request->input('message'),
]);
Mail::raw($this->formatMessage($request), function ($message) use ($request) {
$message->to(config('app.email'))->subject(self::SUBJECT.' from '.$request->input('email'));
$message->from(config('app.email'), $request->input('name'));
});
return redirect()->back()->with('success', 'Your message has been sent.');
}
public function trial(Request $request): RedirectResponse
{
$request->validate([
'name' => 'required',
'email' => 'required|email',
]);
$this->createRecord([
'name' => $request->input('name'),
'email' => $request->input('email'),
'subject' => 'New Trial class request',
'message' => 'Contact to book a trial class.',
]);
Mail::raw($this->formatMessage($request), function ($message) use ($request) {
$message->to(config('app.email'))->subject('New Trial class request from '.$request->input('email'));
$message->from(config('app.email'), $request->input('name'));
});
return redirect()->back()->with('success', "We'll be in touch to book your trial class.");
}
public function formatMessage(Request $request): string
{
return <<<EOF
You have received a new contact message.
Name: {$request->input('name')}
Email: {$request->input('email')}
Message: {$request->input('message')}
EOF;
}
private function createRecord(array $data)
{
Contact::create([
'name' => $data['name'] ?? '?',
'email' => $data['email'] ?? '?',
'subject' => $data['subject'] ?? '?',
'message' => $data['message'] ?? null,
]);
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ImagesController extends Controller
{
public function show(Request $request, string $file)
{
try {
return response()->file($this->imagePath($file));
} catch (\Exception $e) {
abort(404);
}
}
public function subDirectory(Request $request, string $directory, string $file)
{
try {
return response()->file($this->imagePath("{$directory}/{$file}"));
} catch (\Exception $e) {
abort(404);
}
}
private function imagePath(string $file): string
{
return resource_path('images/'.$file);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Models;
use App\Traits\HasActive;
use App\Traits\HasTableName;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasActive;
use HasFactory;
use HasTableName;
public function getRouteKeyName(): string
{
return 'slug';
}
public function view(): Attribute
{
return Attribute::make(
get: fn () => 'blog.'.$this->slug
);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use App\Traits\HasTableName;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
use HasTableName;
protected $guarded = [
'id',
];
#[Scope]
protected function unanswered(Builder $builder): void
{
$builder->where('answered', false);
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use App\Traits\HasActive;
use App\Traits\HasTableName;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Faq extends Model
{
use HasActive;
use HasFactory;
use HasTableName;
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$this->hideSensitiveRequestDetails();
$isLocal = $this->app->environment('local');
$recordAll = config('telescope.record_all');
Telescope::filter(function (IncomingEntry $entry) use ($isLocal, $recordAll) {
return $isLocal ||
$recordAll ||
$entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
}
/**
* Prevent sensitive request details from being logged by Telescope.
*/
protected function hideSensitiveRequestDetails(): void
{
if ($this->app->environment('local')) {
return;
}
Telescope::hideRequestParameters(['_token']);
Telescope::hideRequestHeaders([
'cookie',
'x-csrf-token',
'x-xsrf-token',
]);
}
/**
* Register the Telescope gate.
*
* This gate determines who can access Telescope in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewTelescope', function ($user = null) {
$allowed = explode(',', config('telescope.allowed_ips'));
return in_array(request()->ip(), $allowed);
});
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait HasActive
{
public function scopeActive(Builder $query): Builder
{
return $query->where('active', true);
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace App\Traits;
trait HasTableName
{
public static function tableName(): string
{
return (new static)->getTable();
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class Footer extends Component
{
/**
* Create a new component instance.
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.footer');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class Header extends Component
{
/**
* Create a new component instance.
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.header');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class Meta extends Component
{
/**
* Create a new component instance.
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.meta');
}
}