Laravel 5.4 vendor publish component not working - php

I'm attempting to modify the template for e-mails on an older website I did, running Laravel 5.4
I do eventually plan to update to at least Laravel 5.5, and possibly Laravel 5.7 - but I don't want to do that right now unless strictly necessary (it would involve some significant re-writes to some of my controllers and a lot of extra testing)
I ran:
php artisan vendor:publish --tag=laravel-mail
This created files in resources/views/vendor/mail
I then edited these files and tried sending a message. No change.
I then edited the files in vendor/laravel/framework/src/Illuminate/Mail/resources/views/ and sent a message - the new template showed up.
So despite the existence of the resources/views/vendor/mail folder, Laravel is still reading from the vendor/ folder after running php artisan vendor:publish. How do I fix this? What am I doing wrong?
Update
Some additional info, in case it helps. Here's my mail template (resources/views/mail/email-a-friend.blade.php):
#component('mail::message')
Your friend, {{ $senderName }}, has sent you information about a property they feel you might be interested in.
This property is listed by {{ config('app.name') }}. To view this property and more like it, please click the link below.
#if($agent->id !== $property->agent->id)
[{{ url($property->url()) }}?agent={{ $agent->first_name }}-{{ $agent->last_name }}]({{ url($property->url()) }}?agent={{ $agent->first_name }}-{{ $agent->last_name }})
#else
[{{ url($property->url()) }}]({{ url($property->url()) }})
#endif
#if($text != "")
They also sent this message:
#component('mail::panel')
{{ $text }}
#endcomponent
#endif
#endcomponent
Here's the controller that queues up the e-mail (app/http/Controllers/AjaxController.php - just the relevant function):
public function emailAFriend(Request $request)
{
$property = \App\Models\Property\Property::find($request->input('property-id'));
$agent = $property->agent;
if ($request->input('agent-id') !== $agent->id) {
$agent = \App\User::find($request->input('agent-id'));
}
Mail::to($request->input('send-to'))
->queue(new \App\Mail\EmailAFriend($property, $agent, $request->input('name'), $request->input('reply-to'), $request->input('text')));
return Response::json("success", 200);
}
Here's the Mailable (app/Mail/EmailAFriend.php):
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Models\Property\Property;
use App\User;
class EmailAFriend extends Mailable
{
use Queueable, SerializesModels;
public $subject = "Someone sent you a property!";
public $property;
public $agent;
public $senderName;
public $senderEmail;
public $text;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct(Property $property, User $agent, $name, $email, $text)
{
$this->subject = "$name sent you information about a property";
$this->property = $property;
$this->agent = $agent;
$this->senderName = $name;
$this->senderEmail = $email;
$this->text = $text;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->markdown('emails.email-a-friend')
->replyTo($this->senderEmail, $this->senderName)
->attachData(
$this->property->generatePdf(['agent' => $this->agent])->inline(),
"{$this->property->details->lot_size} acres in {$this->property->location->county} county.pdf",
[
'mime' => 'application/pdf'
]
);
}
}
For testing purposes I'm using the sync QueueDriver, so this sends immediately upon the AJAX request being made. In production I use the database QueueDriver.
Update 2
The components:
resources/views/vendor/mail/html/message.blade.php:
#component('mail::layout')
{{-- Header --}}
#slot('header')
#component('mail::header', ['url' => config('app.url')])
<img src="{{ url('/img/layout/logo.png') }}" alt="{{ config('app.name') }}" />
#endcomponent
#endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
#if (isset($subcopy))
#slot('subcopy')
#component('mail::subcopy')
{{ $subcopy }}
#endcomponent
#endslot
#endif
{{-- Footer --}}
#slot('footer')
#component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
#endcomponent
#endslot
#endcomponent
resources/views/vendor/mail/markdown/message.blade.php:
#component('mail::layout')
{{-- Header --}}
#slot('header')
#component('mail::header', ['url' => config('app.url')])
![{{ config('app.name') }}]({{ url('/img/layout/logo.png') }})
#endcomponent
#endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
#if (isset($subcopy))
#slot('subcopy')
#component('mail::subcopy')
{{ $subcopy }}
#endcomponent
#endslot
#endif
{{-- Footer --}}
#slot('footer')
#component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
#endcomponent
#endslot
#endcomponent
The difference between these two and the default components (vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/message.blade.php and the markdown equivalent) is in the header:
{{ config('app.name') }}
replaced with:
<img src="{{ url('/img/layout/logo.png') }}" alt="{{ config('app.name') }}" />
I was attempting to replace the company name with their logo. When I go into vendor/laravel/framework/src/Illuminate/Mail/resources/views/markdown/message.blade.php and edit this file directly, I do see the logo in the resulting e-mail. So despite the existence of the published component, it's still reading from the vendor/ directory (and editing the vendor/ directory is no good, because then the change won't persist in production)

So after digging through the Laravel source for over an hour, I finally figured this out.
The Markdown renderer loads components from its componentPaths variable
The componentPaths variable gets set by loadComponentFrom()
loadComponentsFrom is called in the constructor of the Markdown renderer and passed $options['paths']
Knowing this I started looking into "Laravel markdown options paths" and found the following: https://stackoverflow.com/a/44264874/436976
I updated config/mail.php and added the recommended lines and it worked perfectly! I feel like vendor:publish should have done this for me, or there should have at least been some mention of this step in the official Laravel documentation, but fortunately I figured this out in under a day - so that's always nice
Note (Further Research)
It turns out this was mentioned in the official Laravel documentation, just not where I expected.
My website was originally a Laravel 5.1 site that was upgraded to 5.2, then to 5.3, and then ultimately to 5.4 before it went live (I never updated to 5.5 because once the site was live I wanted to minimize changes to the underlying framework)
With each Laravel upgrade I kept rolling forward the old files from the config/ directory and I apparently did a poor job of following the upgrade guides, because they are pretty clear:
https://laravel.com/docs/5.4/upgrade
New Configuration Options
In order to provide support for Laravel 5.4's new Markdown mail components, you should add the following block of configuration to the bottom of your mail configuration file:
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
Had I updated my configuration file as directed, I would never have had these issues.

Related

Symfony4 translation in twig

As for Symfony4 translation, thanks to this article. It works well in Controller.
public function index(TranslatorInterface $translator)
{
$translated = $translator->trans('test');// it works
print $translated;exit;
in messages.en.yaml
test: englishtest
However I can't translate message in twig.
<br>
{{ test|trans }}
<br>
It shows the error Variable "test" does not exist.
I need to do something in advance for translation in twig???
The method signature looks like this:
{{ message|trans(arguments = [], domain = null, locale = null) }}
See https://symfony.com/doc/current/reference/twig_reference.html#trans
So if test is not a variable, then {{ 'test'|trans }} should work (as zalex already pointed out).

SlimPHP v3 how to display flash message on view

In their new documentation there isn't anything for flash messages.
I installed the flash extension from their github repository (slimphp/Slim-Flash). Everything works fine, I can add messages and can also get these messages.
// Adding a message
$this->flash->addMessage('test', 'This is a message');
// Getting a message
$this->flash->getMessage('test')[0];
But this only works inside routes. Of course I want to have these messages displayed on my view.
But I just don't know how to get this message on the twig view.
I have already tried:
{{ container.flash.message('test')[0] }}
{{ container.flash.getMessage('test')[0] }}
{{ this.flash.message('test')[0] }}
{{ this.flash.getMessage('test')[0] }}
{{ flash.message('test')[0] }}
{{ flash.getMessage('test')[0] }}
{{ app.flash.message('test')[0] }}
{{ app.flash.getMessage('test')[0] }}
{{ container.flash.test }}
Thanks for help!
You can add the flash message inside the data attribute from the render method:
$this->view->render($res, 'path/to/template.twig', [
'flash' => $this->flash
]);
Or you could add a middleware and add your flash instance to the twig parameters
$app->add(function ($request, $response, $next) {
$this->view->offsetSet("flash", $this->flash);
return $next($request, $response);
});
then it should be possible to access the messages inside the twig template with
{{ flash.getMessage('test') }}

Laravel: render templates in database

My Laravel app allows users to modify email templates, then save them to database.
Sample:
Customer {{ $customer_name }} has accepted your proposed timing {{ $timing }} for PO {{ $po_no }}
(User can change 'customer' to 'client' etc.. but leave the {{ }} intact )
The app then loads dynamic data below into above template.
$data = array('customer_email'=> "email#mail.com",
'timing'=> "2pm",'po_no' => "PO001"
);
Then email those rendered text to some emails.
How can I do that?
Rephrase:
load text from db
make it a Blade template
render that template with data
I get stuck at #2
add email view to your views/emails folder
ex: mail_template.blade.php
Customer {{ $customer_name }} has accepted your proposed
timing {{ $timing }} for PO {{ $po_no }}
send method
Mail::send('emails.mail_template', array('customer_email'=> "email#mail.com", 'timing'=> "2pm",'po_no' => "PO001" ), function($message)
{
$message->to('sendto#gmail.com')->subject('your email subject');
});
further read Official Doc

How do I use the POST of Laravel's Route::resource?

Below is my code for a Laravel 4 project.
Going to the authors/create URL and submitting the form gives me a 405 error.
However, if I prepend the routes.php file with Route::post('authors/store', 'AuthorsController#store');, basically doubling what it already should do, everything works like a charm!
Why do I need do prepend said line in my code to work? I can only assume I'm doing something wrong here.
routes.php:
Route::resource('authors', 'AuthorsController');
AuthorsController.php:
public function create() {
$view = View::make('authors.create');
return $view;
}
public function store() {
//
}
authors/create.twig:
{{ form_open({'url':'authors/store'},{"method" : "post"}) }}
<p>
{{ form_label("Name", "name") }}
{{ form_text("name") }}
</p>
<p>
{{ form_submit("Add Author") }}
</p>
{{ form_close() }}
The store action get's trigger when you POST to the resource. So just authors and not authors/store:
{{ form_open({'url':'authors'},{"method" : "post"}) }}
See this table on more information what URL corresponds to what controller action.
Also I think it should be like this:
{{ form_open({'url':'authors', 'method' : 'post'}) }}
And you can pass the route name Laravel automatically generates to make your life a bit easier:
{{ form_open({'route':'authors.store', 'method' : 'post'}) }}
Oh and one more, post is the default method so this should do as well:
{{ form_open({'route':'authors.store'}) }}

NotFoundHttpException at new route Laravel 4

i see there are similar questions but dont find any clue of me problem.
I created a basic users system, to manage groups, permissions, users, etc. The basic routes like create, edit, delete, index are working.
Now im trying to add one more function to UserController, to manage the users groups in a simple view.
Route::group(array('prefix' => 'admin'), function()
{
Route::resource('groups', 'GroupController');
Route::resource('users', 'UserController');
});
The function in controller:
public function groups($id)
{
$user = Sentry::findUserByID($id);
$groups = $user->getGroups();
return View::make('users.show')
->with('groups', $groups);
}
And the users/groups.blade.php:
#extends('layouts.admin')
#section('content')
<header id="page-title">
<h1>User Groups</h1>
</header>
<!-- if there are creation errors, they will show here -->
{{ HTML::ul($errors->all()) }}
{{ Form::open(array('url' => 'admin/users/save_groups')) }}
<div class="form-group">
</div>
{{ Form::submit('Create!', array('class' => 'btn btn-primary')) }}
{{ Form::button('Cancel', array('class' => 'btn btn-danger')) }}
{{ Form::close() }}
#stop
I go to url "mysite/admin/users/2/groups", and im getting the NotFoundHttpException, i try many ways to make it works and dont know what is happening.
I assume it will works like "mysite/admin/users/2/edit", but if i test the show function, it only is "mysite/admin/users/2", dont need the show action to know is that function, maybe i missed something.
You have declared a route for "GroupsController". As per the documentation, this will only handle actions as defined in the table: "Actions Handled By Resource Controller"
Just by adding one more action it won't simply be extended by Laravel.
You should instead type:
Route::get('users/{id}/groups', 'UserController#groups');

Categories