Git Product home page Git Product logo

laravel-mjml's Introduction

Laravel MJML

Build responsive e-mails easily using MJML and Laravel Mailables.

MJML

MJML is an awesome tool from MailJet that allows us to create responsive emails very easily. For more information on how to use it, head to their documentation here

Installation

To install this package, require this package using composer as follows:

composer require asahasrabuddhe/laravel-mjml

After composer installs the packages and all the dependencies, publish the package configuration using artisan command:

php artisan vendor:publish

Select the laravel-mjml in the list. You will also need to install the MJML CLI:

npm install --save mjml

Getting Started

  1. Create a view containing MJML in your resources/views directory.

  2. Create a mailable class using artisan command: php artisan make:mail MJMLEmail

  3. In the mailable class, replace

    use Illuminate\Mail\Mailable;

    with

    use Asahasrabuddhe\LaravelMJML\Mail\Mailable;

  4. For Laravel 8 and below, in the build method, use:

    public function build()
    {
        return $this->mjml('view.name')
    }

    For Laravel 9, in the content method:, use:

    public function content()
    {
        return new Content(
            view: $this->mjml('view.name')->buildMjmlView()['html'],
        );
    }

Configuration

By default, the package will automatically detect the path of the MJML CLI installed locally in the project. In case this does not happen or the MJML CLI is installed globally, please update the configuration file likewise.

That's it! You have successfully installed and configured the MJML package for use. Just create new views and use them in the mailables class.

laravel-mjml's People

Contributors

asahasrabuddhe avatar dependabot-preview[bot] avatar dependabot[bot] avatar eklundkristoffer avatar gabestah avatar jimping avatar madina972 avatar maximepvrt avatar mbardelmeijer avatar odinti avatar oszilloraptor avatar repat avatar rizaljamhari avatar tennox avatar therooster-pixels avatar umihico avatar vanderb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

laravel-mjml's Issues

html parsing

I'm having this issue with laravel 6
DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity
when try to render a view with mjml.

Does mjml support template inheritance ?

Syntax error: newline unexpected

Hi,

i use mjml in my laravel 5.6 homestead env. I run return $this->mjml('email.register'); in my Mail Class. First i get an Permission denied Error in 'storage/framework/views/. I solved this with sudo chmod -Rvc 755 storage. Now i get the error:

/home/vagrant/project/storage/framework/views/8e5aa8942c86a92b4000071902c0d6c6a7f9d859.php -o /home/vagrant/project/storage/framework/views/e93fbd3b0232a84a0ac393580cf5a8cdafa63541.php\" failed.\n\nExit Code: 2(Misuse of shell builtins)\n\nWorking directory: /home/vagrant/project/public

Output:
================

Error Output:
================
/home/vagrant/project/storage/framework/views/8e5aa8942c86a92b4000071902c0d6c6a7f9d859.php: 2: /home/vagrant/project/storage/framework/views/8e5aa8942c86a92b4000071902c0d6c6a7f9d859.php: Syntax error: newline unexpected

What do i wrong?

Cheers
Ralf

[Question] Confused as to what to do at step 4.

Hi. I am a bit confused as to what to do when I come to step 4: In the build method, use: $this->mjml('view.name');

When I generated the mail class, I cannot see any build method anywhere. When I try to add the build method, VSCode suggests a buildViewData method instead. I added the whole generated mail class below. Sorry if I have misunderstood something.

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Asahasrabuddhe\LaravelMJML\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class EmailVerificationMail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the message envelope.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */
    public function envelope()
    {
        return new Envelope(
            subject: 'Email Verification Mail',
        );
    }

    /**
     * Get the message content definition.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */
    public function content()
    {
        return new Content(
            view: 'view.name',
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array
     */
    public function attachments()
    {
        return [];
    }
}

Bug: Wrong content in Mails through race condition

Hi,
we are running the mail processing in a queue with multiple workers.
We had issues with wrong content in mails. The issue occurs because in
Asahasrabuddhe\LaravelMJML\Process\MJML the $this->path is always the same for a single template which does not work here because you are storing individual rendered content here.

$html = $this->view->render(); // renders the individual content and replaces blade variables with the values File::put($this->path, $html); // here you are storing the individual content under a static path

As long as there is only one process running everything is fine but under load with concurrent processes you will have a race condition with unexpected results.

You should fix it the same way you fixed the caching issue:

$this->path = storage_path("framework/views/{$contentChecksum}.php"); $this->compiledPath = storage_path("framework/views/{$contentChecksum}_compiled.php");
and remove $this->path = ... from the constructor.

Command "mjml" failed

Hi there, I'm having a problem with running the package on Docker. I got the following error while trying to compile MJML view:

The command "/var/www/website/node_modules/.bin/mjml /var/www/website/storage/framework/views/c6bafafe38a9f3777f27996042601e9cd9625e90.php -o /var/www/website/storage/framework/views/7bf0bc40802b37ac7f97db1589dfb01441ebadf6d3ee98928b4434a688e3e597.php" failed.
Exit Code: 127(Command not found)
Working directory: /var/www/website/public
Output: ================
Error Output: ================ /usr/bin/env: 'node': No such file or directory

In file asahasrabuddhe/laravel-mjml/src/Process/MJML.php on line 76, following lines of code:

if (! File::exists($this->compiledPath)) {
    $this->process = new Process($this->buildCmdLineFromConfig());
    $this->process->run();
 
    if (! $this->process->isSuccessful()) {
        throw new ProcessFailedException($this->process);
    }
}

This probably have something to do with not properly configured node and npm on Docker, but I have no idea what is happening.

Caching

Hello, I'm using the current version of your library and it works great if installed with yarn and the path is configured. I've noticed there are some caching issues:
I'm calling the mjml on a blade view lets say password-reset.blade.php, that extends a master mjml-master.blade.php file. If I change the content of the password reset the whole view gets updated. If I change the master file the view doesn't get updated. Also if any include view of the master gets updated there is no change.
Simplified version of the files
// mjml-master.blade.php

<mjml>
    <mj-head>
        @include('emails.partials.db-head')
    </mj-head>
    <mj-body background-color="#fff">
        @yield('content')
    </mj-body>
</mjml>

// password-reset.blade.php

@extends('emails.mjml-master')
@section('content')
    <mj-section padding="20px 20px 20px 20px" background-color="#FFFFFF">
        <mj-column>
            <mj-text align="center" font-weight="300" padding="30px 40px 10px 40px" font-size="28px" line-height="40x"
                     color="#5190c1">Did you forget your Password?
            </mj-text>
        </mj-column>
    </mj-section>
@stop

Error while running process

Hi there, I have an error in file src\Process\MJML.php on line 45:

43     if (! $this->process->isSuccessful()) {
44         throw new ProcessFailedException($this->process);
45     }

And the following error occurs:
Output: ================ Command line error: Error Output: ================ File: C:/xampp/htdocs/projects/project/storage/framework/views/261d91e357c4a5c640dcaba6375f5a729de8a6b6.php TypeError: Cannot set property 'content' of null Input file(s) failed to render openssl config failed: error:02001002:system library:fopen:No such file or directory

Have any idea whats wrong ?

Request: blade processing of <mj-include>-ed files

It would be great to be able to use blade templating also in the files which are included via <mj-include>, but I guess this is a bit difficult, as the blade templating is run before the file is passed to MJML.

One option would be to do it the other way around, but I'm not sure if this would break other use-cases like controlling MJML via blade code first - I could think of a conditional include or adding styling from blade variables, ...

So I thought another option could be to have a folder with MJML partials to be included, and to process them with Blade too, putting them in a subfolder of the compiled views (and using it as --config.filePath), and thus enabling to import the blade-rendered files. ๐ŸŽ‰

What do you think? Feasible? Useful? Better idea?

Might work on a PR if we agree on a solution.

Undefined variable $abc

When I pass a variable to the view content I get this issue

public function content()
    {
        return new Content(
            view: $this->mjml('test')->buildMjmlView()['html'],
            with: [
                'abc' => 'Dummy Sentence'
            ]
        );
    }

<mj-include> doesn't work because of relative paths

I can't use relative includes

<mj-include path="./_template.mjml" />

the MJML command is run on the file

/app/storage/framework/views/96c..817.mjml.php

but the includes are in relative to the view file in

/app/resources/views/emails/view-name.blade.php

(side question: Is there a better option than using .blade.php as extension? .mjml.blade.php did not work)

Are directives supported?

Using this with Laravel 9.x. Everything goes swimmingly until I attempt to use something like a @for or @foreach directive in the Blade view. Then it either doesn't render, or throws an error such as:

DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity, line: 368

So am I doing something wrong, or will directives like that simply not work as it stands right now?

Embedding images fails

Often times with email messages we want to embed the images. Seems it is not possible with this package.

The usual syntax is along the lines of:

<img src="{{ $message->embed(public_path('img/some-random-image.png')) }}" />

This fails with an exception tho:

Undefined variable: message 

Stack trace:

#0 /home/XX/resources/views/emails/users/XX.blade.php(36): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(8, 'Undefined varia...', '/home/XX/s...', 36, Array)
#1 /home/XX/vendor/laravel/framework/src/Illuminate/View/Engines/PhpEngine.php(43): include('/home/XX/s...')
#2 /home/XX/vendor/laravel/framework/src/Illuminate/View/Engines/CompilerEngine.php(59): Illuminate\\View\\Engines\\PhpEngine->evaluatePath('/home/XX/s...', Array)
#3 /home/XX/vendor/facade/ignition/src/Views/Engines/CompilerEngine.php(36): Illuminate\\View\\Engines\\CompilerEngine->get('/home/XX/r...', Array)
#4 /home/XX/vendor/laravel/framework/src/Illuminate/View/View.php(143): Facade\\Ignition\\Views\\Engines\\CompilerEngine->get('/home/XX/r...', Array)
#5 /home/XX/vendor/laravel/framework/src/Illuminate/View/View.php(126): Illuminate\\View\\View->getContents()
#6 /home/XX/vendor/laravel/framework/src/Illuminate/View/View.php(91): Illuminate\\View\\View->renderContents()
#7 /home/XX/vendor/asahasrabuddhe/laravel-mjml/src/Process/MJML.php(64): Illuminate\\View\\View->render()
#8 /home/XX/vendor/asahasrabuddhe/laravel-mjml/src/Mail/Mailable.php(66): Asahasrabuddhe\\LaravelMJML\\Process\\MJML->renderHTML()
#9 /home/XX/vendor/asahasrabuddhe/laravel-mjml/src/Mail/Mailable.php(41): Asahasrabuddhe\\LaravelMJML\\Mail\\Mailable->buildMjmlView()
#10 /home/XX/vendor/laravel/framework/src/Illuminate/Mail/Mailable.php(153): Asahasrabuddhe\\LaravelMJML\\Mail\\Mailable->buildView()

laravel 9 compatibility

This is not installable in laravel 9 due to symfony/process being set to version 4/5 while laravel 9 is locked to v6

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires asahasrabuddhe/laravel-mjml ^0.4.0 -> satisfiable by asahasrabuddhe/laravel-mjml[0.4.0].
    - asahasrabuddhe/laravel-mjml 0.4.0 requires symfony/process ^4.0|^5.0 -> found symfony/process[v4.0.0-BETA1, ..., 4.4.x-dev, v5.0.0-BETA1, ..., 5.4.x-dev] but the package is fixed to v6.0.8 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.

node is not recognised; but it's installed and available from all consoles.

Hi again. Still having some trouble getting this to work.
The error I get is:

The command "node ../node_modules/.bin/mjml D:\Laragon\www\lambda-inertia\storage\framework
\views/5eef33286172620d9fd59fa930e6a35c9a91261383b22ddba6a06b764a0d69ac.mjml.php --config.filePath=D:\Laragon
\www\lambda-inertia\resources\views/emails -o D:\Laragon\www\lambda-inertia\storage\framework
\views/18d7d18a15377d4820fc42047a6c7d749e41c2e5cff55c6380cf251da3e9dc11.php" failed. Exit Code: 1(General error) Working 
directory: D:\Laragon\www\lambda-inertia\public Output: ================ Error Output: ================ 'node' is not
 recognized as an internal or external command, operable program or batch file

I have checked that node is available system wide over all consoles like CMD, PS, Terminal, from the terminal in VSCode even.

Not sure what's causing this.

pass ignore_errors to html2text

It would be nice to be able to pass ignore_errors=true to html2text::convert() in /src/Process/MJML.php in renderText()`.

Laravel 11 support

With the release of Laravel 11 this week I have been testing the compatibility of packages that we already use in our projects using a completely fresh project.

I was wondering when this package would be getting an update to make it compatible with Laravel 11 as at the moment get the following error on composer require:

Your requirements could not be resolved to an installable set of packages.

Problem 1
- asahasrabuddhe/laravel-mjml[dev-dependabot/composer/league/flysystem-1.1.4, dev-dependabot/composer/soundasleep/html2text-1.1.0, dev-dependabot/composer/orchestra/testbench-4.17.0, dev-dependabot/composer/laravel/framework-6.20.30, dev-dependabot/composer/symfony/process-4.4.27, dev-dependabot/composer/php-coveralls/php-coveralls-2.4.3, dev-dependabot/add-v2-config-file, 0.4.0] require symfony/process ^4.0|^5.0 -> found symfony/process[v4.0.0-BETA1, ..., 4.4.x-dev, v5.0.0-BETA1, ..., 5.4.x-dev] but the package is fixed to v7.0.4 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- asahasrabuddhe/laravel-mjml[dev-develop, dev-analysis-x0JP6M, 0.1.0, ..., 0.3.0] require symfony/process ^4.0 -> found symfony/process[v4.0.0-BETA1, ..., 4.4.x-dev] but the package is fixed to v7.0.4 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- asahasrabuddhe/laravel-mjml[dev-dependabot/composer/orchestra/testbench-6.41.0, dev-dependabot/composer/symfony/process-5.4.34, dev-dependabot/composer/php-coveralls/php-coveralls-2.7.0, dev-master, 0.5.0, ..., 0.9.0] require symfony/process ^4.0.0|^5.0.0|^6.0.0 -> found symfony/process[v4.0.0-BETA1, ..., 4.4.x-dev, v5.0.0-BETA1, ..., 5.4.x-dev, v6.0.0-BETA1, ..., 6.4.x-dev] but the package is fixed to v7.0.4 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- Root composer.json requires asahasrabuddhe/laravel-mjml * -> satisfiable by asahasrabuddhe/laravel-mjml[dev-dependabot/composer/league/flysystem-1.1.4, dev-dependabot/composer/soundasleep/html2text-1.1.0, dev-dependabot/composer/orchestra/testbench-4.17.0, dev-dependabot/composer/laravel/framework-6.20.30, dev-dependabot/composer/orchestra/testbench-6.41.0, dev-dependabot/composer/symfony/process-4.4.27, dev-dependabot/composer/symfony/process-5.4.34, dev-dependabot/composer/php-coveralls/php-coveralls-2.4.3, dev-dependabot/composer/php-coveralls/php-coveralls-2.7.0, dev-dependabot/add-v2-config-file, dev-master, dev-develop, dev-analysis-x0JP6M, 0.1.0, ..., 0.9.0, 9999999-dev].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.
You can also try re-running composer require with an explicit version constraint, e.g. "composer require asahasrabuddhe/laravel-mjml:*" to figure out if any version is installable, or "composer require asahasrabuddhe/laravel-mjml:^2.1" if you know which you need.

Config path is not logical

In Process/MJML.php:108 you return the path including 'node'. This should be done in buildCmdLineFromConfig. I will make a PR

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.