mirror of
https://github.com/archtechx/laravel-tips.git
synced 2025-12-11 21:14:02 +00:00
livewire thread
This commit is contained in:
parent
a60370fbc9
commit
57ab16675e
18 changed files with 244 additions and 25 deletions
|
|
@ -4,12 +4,14 @@ namespace App\Console\Commands;
|
|||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Routing\RedirectController;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Routing\UrlGenerator;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
class GenerateHtml extends Command
|
||||
{
|
||||
|
|
@ -44,30 +46,44 @@ class GenerateHtml extends Command
|
|||
->values()
|
||||
->each(function (Route $route) use ($outdir) {
|
||||
if (! count($route->parameterNames())) {
|
||||
if ($route->getActionName() === '\\' . RedirectController::class) {
|
||||
// $action = $this->router->match('get', $route->defaults['destination'])->getAction();
|
||||
// Redirects don't work yet
|
||||
$action = $route->getAction();
|
||||
} else {
|
||||
$action = $route->getAction();
|
||||
}
|
||||
|
||||
if (! is_dir($path = $outdir . '/' . $route->uri())) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
|
||||
File::put($outdir . '/' . $route->uri() . '/index.html', $this->render($action['uses']()));
|
||||
File::put($outdir . '/' . $route->uri() . '/index.html', $this->render($route->getAction()['uses']()));
|
||||
} else {
|
||||
$model = 'App\\Models\\' . ucfirst($parameter = $route->parameterNames()[0]);
|
||||
$model = 'App\\Models\\' . ucfirst($firstParameter = $route->parameterNames()[0]);
|
||||
|
||||
$root = (string) Str::of($outdir . '/' . $route->uri())->before("{{$parameter}}");
|
||||
$root = (string) Str::of($outdir . '/' . $route->uri())->before("{{$firstParameter}}");
|
||||
|
||||
foreach ($model::cursor() as $instance) {
|
||||
if (! is_dir($path = $root . $instance->getRouteKey())) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
|
||||
File::put($path . '/index.html', $this->render($route->getAction()['uses']($instance)));
|
||||
if (count($route->parameterNames()) === 1) {
|
||||
File::put($path . '/index.html', $this->render($route->getAction()['uses']($instance)));
|
||||
} else { // 2 parameters
|
||||
$datasets = [];
|
||||
|
||||
foreach ($route->parameterNames() as $parameter) {
|
||||
if ($parameter === $firstParameter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($instance->{Str::plural($parameter)} as $key => $value) {
|
||||
$datasets[$key] = [$instance, $key];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($datasets as $key => $data) {
|
||||
if (! is_dir($path = $path . '/' . $key)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
|
||||
File::put($path . '/index.html', $this->render($route->getAction()['uses'](...$data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -76,7 +92,13 @@ class GenerateHtml extends Command
|
|||
}
|
||||
|
||||
protected function render ($response) {
|
||||
if ($response instanceof RedirectResponse) {
|
||||
// Redirect responses shouldn't be rendered with headers
|
||||
return $response->getContent();
|
||||
}
|
||||
|
||||
if ($response instanceof Renderable) {
|
||||
// Views should be rendered using render()
|
||||
return $response->render();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ class Thread extends Model
|
|||
|
||||
public $timestamps = false;
|
||||
|
||||
public $casts = [
|
||||
'links' => 'array',
|
||||
];
|
||||
|
||||
public static function schema(Blueprint $table)
|
||||
{
|
||||
$table->string('slug')->unique();
|
||||
|
|
@ -22,6 +26,7 @@ class Thread extends Model
|
|||
$table->string('tweet_id')->nullable();
|
||||
$table->foreignId('author_username')->constrained('authors', 'username');
|
||||
$table->text('content');
|
||||
$table->json('links')->default('{}');
|
||||
$table->timestamp('created_at');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ title: '1 RT = 1 tip'
|
|||
tweet_id: 1308082888324374528
|
||||
author_username: samuelstancl
|
||||
created_at: 2021-04-07T18:16:23+00:00
|
||||
links: {}
|
||||
---
|
||||
A thread of misc tips, originally one tip per one retweet.
|
||||
A thread of misc tips, originally one tip per one retweet.
|
||||
|
|
|
|||
|
|
@ -4,5 +4,9 @@ title: 'Laravel Clean Code Tactics'
|
|||
tweet_id: 1272822437181378561
|
||||
author_username: samuelstancl
|
||||
created_at: 2021-04-07T18:16:07+00:00
|
||||
links:
|
||||
download:
|
||||
name: Printable versions
|
||||
url: https://gum.co/laravel-clean-code
|
||||
---
|
||||
The OG thread. Get printable PDF versions [here](https://gum.co/laravel-clean-code).
|
||||
The OG thread. Get printable PDF versions [here](https://gum.co/laravel-clean-code).
|
||||
|
|
|
|||
15
content/threads/obscure-livewire-tips.md
Normal file
15
content/threads/obscure-livewire-tips.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
slug: obscure-livewire-tips
|
||||
title: 'Obscure Livewire Tips'
|
||||
tweet_id: 1380538340982788096
|
||||
author_username: samuelstancl
|
||||
created_at: 2021-04-09T15:19:23+02:00
|
||||
links:
|
||||
download:
|
||||
name: Download code
|
||||
url: https://gum.co/livewire-tips
|
||||
---
|
||||
|
||||
Little-known Livewire tips.
|
||||
|
||||
Get the supporting resources, code examples, and Blade directives [here](https://gum.co/livewire-tips).
|
||||
15
content/tips/advanced-notifications.md
Normal file
15
content/tips/advanced-notifications.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
title: 'Advanced notifications'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/AAMo0d/download'
|
||||
created_at: 2021-04-09T17:24:56+02:00
|
||||
slug: advanced-notifications
|
||||
---
|
||||
|
||||
This trait allows dispatching notifications:
|
||||
- on the current page
|
||||
- on the next page (after redirect)
|
||||
- from any part of your code: you can run Lean::notify() in an action class, a model method, or anywhere else — and it will be sent to the browser
|
||||
16
content/tips/advanced-trait-design.md
Normal file
16
content/tips/advanced-trait-design.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: 'Advanced trait design'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/YjzF2w/download'
|
||||
created_at: 2021-04-09T17:23:56+02:00
|
||||
slug: advanced-trait-design
|
||||
---
|
||||
|
||||
Let's look at the same trait again. It does two interesting things.
|
||||
|
||||
1. Livewire::listen() is superior to hydrate* and dehydrate* when you need access to specific parts of the lifecycle
|
||||
|
||||
2. The `instanceof self` check scopes the listener to the component
|
||||
14
content/tips/call-methods-from-javascript.md
Normal file
14
content/tips/call-methods-from-javascript.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: 'Call methods from JavaScript'
|
||||
tweet_id: '1380545900020436996'
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://pbs.twimg.com/media/EyivLICXIAEX-40.jpg'
|
||||
created_at: 2021-04-09T17:11:56+02:00
|
||||
slug: call-methods-from-javascript
|
||||
---
|
||||
|
||||
Livewire's properties are extremely powerful, which often makes us forget that we can also call methods from the frontend.
|
||||
|
||||
Livewire gives you a full component API. Use it!
|
||||
16
content/tips/compose-components-using-traits.md
Normal file
16
content/tips/compose-components-using-traits.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: 'Compose components using traits'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/j3Jtnw/download'
|
||||
created_at: 2021-04-09T17:12:56+02:00
|
||||
slug: compose-components-using-traits
|
||||
---
|
||||
|
||||
Traits are a powerful way to reuse functionality between Livewire components.
|
||||
|
||||
They're generally better than component nesting, since that comes with more complexity and worse performance.
|
||||
|
||||
Next up: Advanced trait examples
|
||||
14
content/tips/custom-response-effects.md
Normal file
14
content/tips/custom-response-effects.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: 'Custom response effects'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/YjzF2w/download'
|
||||
created_at: 2021-04-09T17:22:56+02:00
|
||||
slug: custom-response-effects
|
||||
---
|
||||
|
||||
You can add custom data to response effects. Those are separate from component data, and act more like events.
|
||||
|
||||
You can use them when you have some JS code that looks at Livewire responses, and when you want more control than dispatching browser events
|
||||
18
content/tips/livewire-readonly-properties.md
Normal file
18
content/tips/livewire-readonly-properties.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: 'Readonly properties'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/oG5FqU/download'
|
||||
created_at: 2021-04-09T17:28:56+02:00
|
||||
slug: livewire-readonly-properties
|
||||
---
|
||||
|
||||
They're a great way to make your components safer and faster.
|
||||
|
||||
Say you have a component for editing some resource.
|
||||
|
||||
You enforce the ACL in mount().
|
||||
|
||||
But if the user could change the currently edited resource, you'd need to enforce ACL on *all* requests!
|
||||
14
content/tips/replace-state-with-a-custom-directive.md
Normal file
14
content/tips/replace-state-with-a-custom-directive.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: 'Replace children with a custom directive'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/w6vdHe/download'
|
||||
created_at: 2021-04-09T17:27:56+02:00
|
||||
slug: replace-children-with-a-custom-directive
|
||||
---
|
||||
|
||||
Imagine that you have a nested component that depends on the parent state. You don't need to sync child state into parent, you just want to replace the child when the parent state changes.
|
||||
|
||||
You can use my custom directive for that.
|
||||
15
content/tips/share-state-using-sprucewire.md
Normal file
15
content/tips/share-state-using-sprucewire.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
title: 'Share state with Sprucewire'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images: {}
|
||||
created_at: 2021-04-09T17:26:56+02:00
|
||||
slug: share-state-with-sprucewire
|
||||
---
|
||||
|
||||
If you have two components that share some state, and you'd like to keep them in sync in a more effective way than dispatching listeners back and forth, check out Sprucewire.
|
||||
|
||||
It works almost like Vuex for Livewire.
|
||||
|
||||
https://twitter.com/_joshhanley/status/1362646916333334528
|
||||
14
content/tips/use-wire-entangle-instead-of-entangle.md
Normal file
14
content/tips/use-wire-entangle-instead-of-entangle.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: 'Quick tip: Use $wire.entangle() instead of @entangle'
|
||||
tweet_id: ''
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://cln.sh/tjz7SC/download'
|
||||
created_at: 2021-04-09T17:25:56+02:00
|
||||
slug: use-wire-entangle-instead-of-entangle
|
||||
---
|
||||
|
||||
It's good to get into the habit of using $wire, since it provides a full communication layer for your Livewire component.
|
||||
|
||||
It also doesn't run into issues when you use it inside single quoted attributes.
|
||||
14
content/tips/use-wire-replace.md
Normal file
14
content/tips/use-wire-replace.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: 'Use wire:replace'
|
||||
tweet_id: '1380542122105921536'
|
||||
thread_slug: obscure-livewire-tips
|
||||
author_username: samuelstancl
|
||||
images:
|
||||
- 'https://pbs.twimg.com/media/EyirvPAWQAIN6ib.jpg'
|
||||
created_at: 2021-04-09T16:11:56+02:00
|
||||
slug: use-wire-replace
|
||||
---
|
||||
|
||||
As you know, Livewire sometimes runs into painful DOM diffing issues. They can usually be solved with simple workarounds, but sometimes those workarounds get quite complex.
|
||||
|
||||
So I built a package that adds a `wire:replace` directive, telling Livewire to replace an entire chunk of the DOM, instead of trying to diff individual changes.
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
@props(['tip'])
|
||||
@props([
|
||||
'tip',
|
||||
'links' => [],
|
||||
])
|
||||
|
||||
<div class="flex w-full p-6 space-x-6 bg-gray-50 rounded-2xl">
|
||||
<a href="{{ $tip->author->profile_url }}" target="_blank">
|
||||
|
|
@ -23,15 +26,30 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500">Tweet</dt>
|
||||
@if($tip->tweet_id)
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500">Tweet</dt>
|
||||
<dd>
|
||||
<x-link href="{{ $tip->tweet_url }}" target="_blank">
|
||||
twitter.com/...
|
||||
</x-link>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<dd>
|
||||
<x-link href="{{ $tip->tweet_url }}" target="_blank">
|
||||
twitter.com/...
|
||||
</x-link>
|
||||
</dd>
|
||||
</div>
|
||||
@if($links)
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500">Links</dt>
|
||||
|
||||
<dd>
|
||||
@foreach($links as $link)
|
||||
<x-link href="{{ $link['url'] }}" target="_blank">
|
||||
{{ ucfirst($link['name']) }}
|
||||
</x-link>
|
||||
@endforeach
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</aside>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<x:layout :title="$thread->title">
|
||||
<x:layout :title="$thread->title" :preview="$thread->title">
|
||||
<header class="relative py-24">
|
||||
<div
|
||||
class="absolute w-full transform skew-y-12 pointer-events-none h-72 md:h-96 -translate-y-1/4 bg-gradient-to-br from-yellow-300 to-pink-400 mix-blend-multiply">
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<section class="py-12 bg-white md:py-24">
|
||||
<x:container>
|
||||
<x:author-card :tip="$thread" />
|
||||
<x:author-card :tip="$thread" :links="$thread->links" />
|
||||
</x:container>
|
||||
</section>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ Route::middleware('static')->group(function () {
|
|||
return view('tips.index', ['tips' => Tip::all()]);
|
||||
})->name('tip.index');
|
||||
|
||||
Route::get('/threads/{thread}/{link}', function (Thread $thread, string $link) {
|
||||
return redirect($thread->links[$link]['url']);
|
||||
})->name('thread.show');
|
||||
|
||||
Route::get('/threads/{thread}', function (Thread $thread) {
|
||||
return view('threads.show', [
|
||||
'thread' => $thread,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue