Skip to content

Instantly share code, notes, and snippets.

@kikoseijo
Last active April 12, 2018 23:31
Show Gist options
  • Save kikoseijo/0ec3bf3f2e0e0dd51f3834414508abf8 to your computer and use it in GitHub Desktop.
Save kikoseijo/0ec3bf3f2e0e0dd51f3834414508abf8 to your computer and use it in GitHub Desktop.
Laravel Contact Form - Form request with validations, back office admin views, recipients database relationship, ajax request using Axios, tests provided by Laravel Dusk. Includes Model files and migrations.
<div class="row">
<div class="col-sm-3">
{!! Former::text('name')->required()->label('First name')->readonly() !!}
</div>
<div class="col-sm-3">
{!! Former::text('last_name')->required()->label('Last name')->readonly() !!}
</div>
<div class="col-sm-3">
{!! Former::email('email')->required()->label('Email')->readonly() !!}
</div>
<div class="col-sm-3">
{!! Former::email('telephone')->required()->label('Phone')->readonly() !!}
</div>
</div>
@include('klaravel::ui.forms.textarea',[
'name' => 'message',
'label' => 'Comments',
])
{{-- @include('klaravel::ui.forms.buttons') --}}
<?php
namespace App\Http\Controllers\Back;
use App\Mail\Admin\ContactFormSubmitted;
use App\Models\FormResponse;
use App\Repositories\FormResponseRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class FormResponseController extends BaseKrudController
{
public function __construct(FormResponseRepository $repo)
{
$this->repo = $repo;
$this->path = 'formresponse';
}
public function resend(Request $request, FormResponse $form_response, $mode = 'admin')
{
if ($mode == 'admin') {
Mail::send(new ContactFormSubmitted($form_response));
} elseif ($mode == 'user') {
Mail::send(new ContactFormSubmitted($form_response));
} elseif ($mode == 'ambos') {
// Mail::send(new ContactFormSubmitted($form_response));
Mail::send(new ContactFormSubmitted($form_response));
}
if ($request->expectsJson()) {
return response()->json(['success' => true]);
}
return back()->with('flash_message', 'Email sent succesfully');
}
public function download()
{
FormResponse::downloadAll();
}
}
@extends('klaravel::layouts.crud')
@php($recipients = array_column(recipients('contactForm'), 'email'))
@section('content')
<div class="{{$crudWrapperClass}}">
@card(['title' => 'Contact forms', 'class' => 'pb-5'])
@component('klaravel::ui.tables.actions-menu', [
'model_name' => $model_name
])
@if ($records->total()>0)
<li class="nav-item mr-3">
<a class="nav-link text-primary"
data-toggle="tooltip"
title="Descargar un Excel con todos los formularios"
onclick="event.preventDefault(); document.getElementById('descarga-xml').submit();"
href="#stop"
>
<i class="far fa-download" aria-hidden="true"></i> &nbsp;Descargar excel
</a>
</li>
@endif
@endcomponent
@if ($records->total()>0)
{!! Former::open()->method('POST')->id('descarga-xml')->route('formresponse.download') !!}
{!! Former::close() !!}
@includeIf('klaravel::ui.tables.pagination',['class'=> 'py-2 mt-2'])
<div class="table-responsive">
@includeIf($viewsBasePath.$model_name.'.table')
</div>
@else
@includeif('klaravel::_parts.no-records')
@endif
@endcard
</div>
@endsection
@php
$iIcons = config('molino.app.formsIcons');
// $receptores = App\Models\Recipient::all()->pluck('form', 'email');
@endphp
<table class="{{config('ksoft.style.table_style')}}">
<caption class="text-right">@includeIf('klaravel::ui.tables.count')</caption>
<tbody>
@foreach($records as $item)
<tr data-toggle="collapse" data-target="#collapse-{{$item->id}}">
<td class="text-muted text-center align-middle">
<span data-toggle="tooltip" title="{{model_title($item->tipo)}}">
{!! array_get($iIcons, $item->tipo) !!}
</span>
</td>
<td style="min-width:220px;" class="pl-2">
<a href="{{route($model_name.'.edit', $item->id)}}">{{ $item->name }}</a>
<br />
<span class="font-weight-light text-muted ">
<small><i class="far fa-clock"></i> {{diff_date_for_humans($item->created_at)}}</small>
</span>
</td>
<td style="min-width:500px">
<div class="excerpt text-truncate" style="max-width:600px;">
{{ $item->message }}
</div>
<span>
<span class="font-weight-light text-muted ">
<small>
@php ($receptores = array_pluck(recipients($item->tipo), 'email'))
<i class="fas fa-envelope"></i> &nbsp;{{ implode(', ', $receptores) }}
</small>
</span>
</span>
</td>
@component('klaravel::ui.tables.actions', [
'size' => 'sm',
'model_name' => $model_name,
'item' => $item
])
<a href="mailto:{{$item->email}}" data-toggle="tooltip" title="Email {{$item->email}}" class="btn btn-primary btn-sm text-white">
<i class="fas fa-paper-plane"></i>
</a>
<a href="tel:{{$item->telephone}}" data-toggle="tooltip" title="Phone {{$item->telephone}}" class="btn btn-primary btn-sm text-white">
<i class="fas fa-phone"></i>
</a>
<a href="#no.action" onclick="resendEmail({{$item->id}})" data-toggle="tooltip" title="Test Email for admin" class="btn btn-primary btn-sm text-white">
<i class="fas fa-notes-medical"></i>
</a>
@endcomponent
</tr>
@endforeach
</tbody>
</table>
@push('stylesheets')
<style media="screen">
.klara-bt-group .btn {
padding-right: 12px;
padding-left: 12px;
}
.table .font-weisssght-light small{
color: #6c757d;
}
.table .expandable {
-webkit-transition: all .85s ease-in-out;
transition: all .85s ease-in-out;
transition: all .85s ease-in-out;
transition: all .85s ease-in-out
}
</style>
@endpush
@push('scripts')
@include('back.formresponse.script-mail-resend')
@endpush
<div id="message-contact"></div>
{!! Former::open()
->route('contact_submit')
->id('formulario-contacto')
->method('POST')
->novalidate(noValidateOnTests())
->rules([
// 'name' => 'required|min:3|max:225',
// 'email' => 'required|email',
// 'message' => 'required'
])
!!}
{{ Former::populate( request() ) }}
<div class="row">
<div class="col-md-6 col-sm-6">
{!! Former::text('name_contact')
->required()
->label(__('contact.first_name'))
->placeholder(__('contact.enter_name'))
!!}
</div>
<div class="col-md-6 col-sm-6">
{!! Former::text('lastname_contact')
->required()
->label(__('contact.last_name'))
->placeholder(__('contact.enter_last_name'))
!!}
</div>
</div>
<!-- End row -->
<div class="row">
<div class="col-md-6 col-sm-6">
{!! Former::text('email_contact')
->required()
->label(__('contact.email'))
->placeholder(__('contact.enter_email'))
!!}
</div>
<div class="col-md-6 col-sm-6">
{!! Former::text('phone_contact')
->required()
->label(__('contact.Phone'))
->placeholder(__('contact.enter_phone'))
!!}
</div>
</div>
<div class="row">
<div class="col-md-12">
{!! Former::textarea('message_contact')
->label(__('contact.Message'))
->placeholder(__('contact.write_message'))
->style('height:150px;')
->rows(4)
!!}
</div>
</div>
<div class="row add_bottom_30">
<div class="col-md-6">
<div class="form-group">
<label>{{ __('contact.human_verfication') }}</label>
<input type="text" id="verify_contact" class=" form-control add_bottom_30" placeholder="{{ __('contact.human_question') }} 3 + 1 =">
</div>
</div>
</div>
<div class="text-center">
<input type="submit" value="{{ __('contact.Submit') }}" class="btn_1" id="submit-contact" dusk="contact-form-button">
</div>
{!! Former::close() !!}
@push('scripts')
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script type="text/javascript">
var contactForm = $('#formulario-contacto');
contactForm.on('submit', function(e) {
e.preventDefault();
var name = $('#name_contact').val();
var last_name = $('#lastname_contact').val();
var email = $('#email_contact').val();
var phone = $('#phone_contact').val();
var message = $('#message_contact').val();
axios.post('{{route('contact_submit')}}', {
name: name,
last_name: last_name,
email: email,
phone: phone,
message: message,
})
.then(function (response) {
if (response.data && response.data.success) {
$('#name_contact').val('');
$('#lastname_contact').val('');
$('#email_contact').val('');
$('#phone_contact').val('');
$('#message_contact').val('');
swal("{{__('contact.Thanks')}}", "{{__('contact.form_sent_response')}}", "success");
} else {
window.Larapp.processErrors(response);
}
})
.catch(function (error) {
window.Larapp.processErrors(error);
});
});
</script>
@endpush
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Referer\Referer;
use App\Http\Requests\ContactFormRequest;
use App\Models\FormResponse;
use App\Mail\Admin\ContactFormSubmitted;
use Illuminate\Support\Facades\Mail;
class ContactController extends Controller
{
public function contactPage(Request $request)
{
return view('front.contact');
}
public function mailToAdmin(ContactFormRequest $message)
{ //send the admin an notification
$formResponse = FormResponse::create([
'name' => array_get($message, 'name'),
'last_name' => array_get($message, 'last_name'),
'email' => array_get($message, 'email'),
'message' => array_get($message, 'message'),
'telephone' => array_get($message, 'phone'),
// 'address' => array_get($message, 'address'),
// 'postal' => array_get($message, 'postal'),
// 'city' => array_get($message, 'city'),
'tipo' => array_get($message, 'tipo') ?: 'contactForm',
'referer' => app(Referer::class)->get(),
]);
$this->processMails(ContactFormSubmitted::class, $formResponse, $formResponse->tipo);
// Mail::send(new ContactFormSubmitted($formResponse));
activity($formResponse->tipo)->log("<a href=\"mailto:{$formResponse->email}\">{$formResponse->email}</a> nuevo formulario <strong>{$formResponse->name}</strong>");
return $this->returnResponse($message);
}
protected function processMails($mailable, $mailData, $referrersTipo)
{
Mail::send(new $mailable($mailData));
}
protected function returnResponse($formRequest)
{
if ($formRequest->expectsJson()) {
return response()->json(['success'=>true]);
}
return redirect()->back()->with('flash_message', __('contact.form_sent_response'));
}
}
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactFormRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|max: 255',
'last_name' => 'required|max: 255',
'email' => 'required|email|max: 255',
'phone' => 'required|max: 255',
'message' => 'required',
];
}
}
@component('mail::message')
# Nuevo contacto
Hola,
Un usuario ha enviado un formulario en [{{ Request::getHost() }}]({{ url('/') }}).
@component('mail::panel')
### Datos del solicitante
Name: {{ $form->name }} <br>
Last name: {{ $form->last_name }} <br>
Telephone: {{ $form->telephone }} <br>
E-mail: {{ $form->email }} <br>
@if ($form->message)
### Comentarios del solicitante
{{ $form->message }} <br>
@endif
---
Origen de la solicitud: {{ $form->referer ?: 'Desconocida' }} <br>
@endcomponent
@slot('subcopy')
<br>
Puedes ver este y otros mensajes anteriores en la siguiente
dirección web [{{ config('app.url') }}]({{ route('formresponse.index') }}).
@endslot
@endcomponent
<?php
namespace App\Mail\Admin;
use App\Models\FormResponse;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ContactFormSubmitted extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $form;
public function __construct(FormResponse $form)
{
$this->form = $form;
}
public function build()
{
$toMail = recipients($this->form->tipo);
$formType = title_case($this->form->tipo);
$toName = $toMail[0]['name'];
$requestEmail = $this->form->email;
activity('mails')->log("Contact request <strong>{$formType}</strong> for {$toName} from {$requestEmail}");
if ($requestEmail == TEST_EMAIL) {
$toNames = array_pluck($toMail, 'name');
$toMail = [[
'email' => DEV_EMAIL,
'name' => implode(' + ', $toNames),
]];
}
return $this
->to($toMail)
->subject('New ' . $formType . ' from ' . $requestEmail . ' in ' . config('app.url'))
->markdown('mails.admin.' . $this->form->tipo . 'Submitted')
->with([
'departmentName' => $toName,
]);
}
}
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use App\Models\User;
class ContactFormTest extends DuskTestCase
{
/**
* @group contact_form
*
* @return void
*/
public function testContactPageAvailable()
{
$user = factory(User::class)->make([
'email' => TEST_EMAIL,
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/')
->waitUntilMissing('#preloader')
->assertSee('Sunnyface.com')
->clickLink(__('menu.Contact'))
->waitUntilMissing('#preloader')
->press('.js-cookie-consent-agree')
->assertSee(__('contact.first_name'))
// ->click('@contact-form-button')
// ->pause(5000)
;
// contact-form-button
$browser
->type('name_contact', $user->name)
->type('email_contact', $user->email)
->type('phone_contact', '6969696969')
->type('message_contact', 'Im a robot!, just doing some automatized test on some critical parts of your website. Ill delete myself latter, sorry for the interuption.')
->press('@contact-form-button')
// ->pause(10000)
->waitForText(__('validation.required', ['attribute' => 'last name']))
->type('lastname_contact', $user->name)
->type('email_contact', 'im not an email')
->press('@contact-form-button')
->waitForText(__('validation.email', ['attribute' => 'email']))
->type('email_contact', $user->email)
->press('@contact-form-button')
->waitFor('.swal-modal .swal-title', 3)
->assertSeeIn('.swal-modal .swal-title', __('contact.Thanks'))
->assertInputValue('name_contact', '');
});
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
// ka make:migration create_form_responses_table
class CreateFormResponsesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('form_responses', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 255);
$table->string('last_name', 255);
$table->string('email', 100);
$table->unsignedInteger('ref_id')->nullable();
$table->string('tipo', 40)->default('contactForm');
$table->string('telephone', 255)->nullable();
$table->string('address', 255)->nullable();
$table->string('postal', 255)->nullable();
$table->string('city', 255)->nullable();
$table->text('message', 64)->nullable();
$table->string('referer')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('form_responses');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
// ka make:migration create_recipients_table
class CreateRecipientsTable extends Migration
{
public function up()
{
Schema::create('recipients', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->string('form')->nullable();
$table->string('email')->nullable();
$table->boolean('draft')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down()
{
Schema::drop('recipients');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Maatwebsite\Excel\Facades\Excel;
use Spatie\Referer\Referer;
class FormResponse extends Model
{
protected $guarded = ['id'];
public static function boot()
{
static::creating(function (FormResponse $formResponse) {
$formResponse->referer = app(Referer::class)->get();
});
}
public static function downloadAll()
{
Excel::create('Responses ' . date('Y-m-d'), function ($excel) {
$excel->sheet('Responses', function ($sheet) {
$sheet->freezeFirstRow();
$sheet->cells('A1:Z1', function ($cells) {
$cells->setFontWeight('bold');
$cells->setBorder('node', 'none', 'solid', 'none');
});
$sheet->fromModel(self::all());
});
})->download('xlsx');
}
public function recipient()
{
return $this->belongsTo('App\Models\Recipient', 'tipo', 'form');
}
}
<div class="row">
<div class="col-sm-4">
{!! Former::text('name')->required()->label('Name') !!}
</div>
<div class="col-sm-4">
{!! Former::text('email')->label('Receptores del email') !!}
</div>
@if(isset($record))
<div class="col-sm-4">
{!! Former::text('form')->label('FormKey ID')->readonly() !!}
</div>
@else
<div class="col-sm-4">
{!! Former::text('form')->label('FormKey ID') !!}
</div>
@endif
</div>
@include('klaravel::ui.forms.buttons')
<table class="{{config('ksoft.style.table_style')}}">
<thead class="thead-dark">
<tr>
<th>#</th>
<th></th>
<th class="text-center"><i class="fal fa-chart-bar"></i></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($records as $item)
<tr>
<th scope="row">{{ $item->id }}</th>
<td><a href="{{route($model_name.'.edit', $item->id)}}">{{ $item->name }}</a></td>
<td class="text-center">{{ $item->forms_count }}</td>
<td>{{ $item->form }}</td>
<td>{{ $item->email }}</td>
@include('klaravel::ui.tables.actions',['size' => 'sm'])
</tr>
@endforeach
</tbody>
</table>
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Recipient extends Model
{
protected $guarded = ['id'];
public static $rules = [
'name' => 'required|string|max:255',
'form' => 'required|string|max:255',
'email' => 'required|string|max:255',
];
public static function forForm(string $form): array
{
if (!app()->environment('production')) {
return config('forms.development_recipients');
}
$records = static::where('form', $form)->get();
return $records->map(function (Recipient $recipient) {
return [
'email' => $recipient->email,
'name' => $recipient->name,
];
})->toArray();
}
public function forms()
{
return $this->hasMany('App\Models\FormResponse', 'tipo', 'form');
}
}
<?php
use Illuminate\Database\Seeder;
use App\Models\Recipient;
class RecipientSeeder extends Seeder
{
public function run()
{
// $this->truncate((new Recipient())->getTable());
Recipient::create([
'form' => 'contactForm',
'email' => DEV_EMAIL,
'draft' => false,
]);
}
}
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script type="text/javascript">
function resendEmail(recordID) {
swal({
title: "Reenvio de email",
text: "Selecciona a quien ha de mandar el email",
icon: "info",
buttons: {
admin: "Admin",
user: "User",
ambos: "Both",
cancel: "Cancel",
}
})
.then((action) => {
if (!action)
return;
window.axios
.get(`{{route($model_name.'.resend')}}/${recordID}/${action}/`)
.then(json => {
if (json.data && json.data.success){
swal("Enhorabuena", "La tarea se ha realizado correctamente", "success");
} else {
swal("Error", "Algo no ha salido como debiera-", "error");
}
})
.catch(error => {
if (error) {
swal("Oh noes!", "The AJAX request failed!", "error");
window.Larapp.processErrors(error);
} else {
swal.stopLoading();
swal.close();
}
});
});
}
</script>
@kikoseijo
Copy link
Author

Missing files and helpers are provided by https://github.com/kikoseijo/klaravel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment