Pour installer le projet, on lance la commande composer depuis le terminal:
composer create-project themosis/themosis larajobs
cd larajobs
# Dans la version 2.0 de Themosis, le thème de base n'est plus installé par défaut
# La nouvelle invité de commande permet de l'installer facilement
php console theme:install larajobs
On doit modifier les variables d'environnement avant de procéder à l'installation du framework
Dans le fichier .env
, on va modifier les informations de base de données puis les URL de l'application
APP_NAME=Larajobs
MAIL_ENCRYPTION=null
MAIL_PORT=1025
DATABASE_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
Puis, installation du projet en nous rendant sur l'addresse principale de notre site-web.
Notre root directory est dans le dossier htdocs/cms
, dossier dans lequel est installé WordPress. Comme pour les anciennes versions de Themosis, il est toujours possible de développer nos thèmes et plugins dans ce dossier.
Mais pour faciliter la gestion de notre projet, les développements doivent maintenant se faire dans le dossier app
- Explication de la structure et l'organisation d'un projet Themosis
- Différence entre les répertoires root view VS theme view
- La commande make disponible dans la console
- Ajouter un paramètre dans l'url:
Route::get(/posts/{slug})
- Méthodes pour les routes:
Route::post()
- Route qui charge une view:
Route::view('/contact', 'contact')
The WordPress $post and $query parameters:
Route::get('page', function ($post, $query) {
return view('pages', [
'page' => $post
]);
});
- Page d'accueil:
Route::get('home')
- Route pour une page spéciale:
Route::get('page', ['contact', 24, function(){}])
- Route pour un template:
Route::get('template', ['all-jobs'])
- Single custom post route:
Route::get('singular', ['jobs'])
Voir le fichier app.php de configuration du projet
- Installation de laravel-mix
- Configuration du fichier webpack-mix.js: on va désactiver les notifications avec la méthode
mix.disableNotifications()
- Installation de Tailwind.css:
npm install tailwind
et./node_modules/.bin/tailwind init
- Dans le fichier webpack-mix.js, ajouter après sass les options de compilation suivantes:
mix.sass('assets/sass/style.sass', 'dist/css/theme.css').options({
processCssUrls: false,
postCss: [
tailwindcss('./tailwind.js') ],
});
- Dans le fichier sass, on va include tailwind en utilisant les commandes suivantes:
@tailwind preflight
@tailwind components
@tailwind utilities
<header class="bg-red-light text-white">
<div class="container mx-auto py-4">
<h1 class="w-1/2 w-auto text-2xl font-medium">Larajobs Montreal</h1>
</div>
<div class="block bg-white border-b">
<div class="container flex mx-auto">
<div class="flex -mb-px mr-8">
<a href="#" class="no-underline text-red-light flex items-center py-4 border-b border-red-light">
<svg class="h-6 w-6 fill-current mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 8H5.5v-.5l11-.88v.88H18V6c0-1.1-.891-1.872-1.979-1.717L5.98 5.717C4.891 5.873 4 6.9 4 8v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2zm-1.5 7.006a1.5 1.5 0 1 1 .001-3.001 1.5 1.5 0 0 1-.001 3.001z" fill-rule="nonzero"></path></svg>
<span class="label">Tous les emplois</span>
</a>
</div>
<div class="flex -mb-px mr-8">
<a href="#" class="no-underline text-white opacity-50 md:text-grey-dark md:opacity-100 flex items-center py-4 border-b border-transparent hover:opacity-100 md:hover:border-grey-dark">
<svg class="h-6 w-6 fill-current mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.783 12c0-1.049.646-1.875 1.617-2.443a8.932 8.932 0 0 0-.692-1.672c-1.089.285-1.97-.141-2.711-.883-.741-.74-.968-1.621-.683-2.711a8.732 8.732 0 0 0-1.672-.691c-.568.97-1.595 1.615-2.642 1.615-1.048 0-2.074-.645-2.643-1.615-.58.172-1.14.403-1.671.691.285 1.09.059 1.971-.684 2.711-.74.742-1.621 1.168-2.711.883A8.797 8.797 0 0 0 3.6 9.557c.97.568 1.615 1.394 1.615 2.443 0 1.047-.645 2.074-1.615 2.643.173.58.404 1.14.691 1.672 1.09-.285 1.971-.059 2.711.682.741.742.969 1.623.684 2.711.532.288 1.092.52 1.672.693.568-.973 1.595-1.617 2.643-1.617 1.047 0 2.074.645 2.643 1.617a8.963 8.963 0 0 0 1.672-.693c-.285-1.088-.059-1.969.683-2.711.741-.74 1.622-1.166 2.711-.883.287-.532.52-1.092.692-1.672-.973-.569-1.619-1.395-1.619-2.442zM12 15.652a3.653 3.653 0 1 1 0-7.306 3.653 3.653 0 0 1 0 7.306z" fill-rule="nonzero"></path></svg>
<span class="label">Réglages</span>
</a>
</div>
</div>
</div>
</header>
<div class="bg-white border-t">
<div class="container mx-auto flex justify-between items-center text-sm">
<div class="text-left py-4">
<a href="#" class="no-underline text-grey-dark mr-4">Tous les emplois</a>
<a href="#" class="no-underline text-grey-dark mr-4">Réglages</a>
<a href="#" class="no-underline text-grey-dark">Contact</a>
</div>
<div class="text-grey text-center py-4">Meetup Laravel Montréal</div>
</div>
</div>
@component('components.card', ['test' => 'HELLO'])
My components with errors
@slot('header')
HEADER
@endslot
@endcomponent
<div class="flex-grow flex flex-col bg-white border-b sm:rounded sm:border shadow overflow-hidden">
@isset($header)
<div class="border-b">
<div class="flex justify-between px-4"> {{ $header }}</div>
</div>
@endisset
{{ $slot }}
@isset($footer)
<div class="px-4 py-10 text-grey">{{ $footer }}</div>
@endisset
</div>
On va créer un hook avec la commande php console make:hook JobsCustomPostType
Puis on va ajouter le hook dans le fichier de configuration app.php
dans la section hooks.
On intègre ensuite la facade à nos dépendances Themosis\Support\Facades\PostType
puis on utilise la méthode PostType::make
PostType::make('jobs', 'Jobs', 'Job')->setArguments(['menu_icon' => 'dashicons-store'])->set();
Pour créer une metabox, on intègre la facade à nos dépendances Themosis\Support\Facades\MetaBox
puis on utilise la méthode MetaBox::make
Metabox::make('properties', 'jobs')->setTitle('Paramètres du job')->set();
Pour créer des fields, on importe la facade Themosis\Support\Facades\Field
. On importe également la class Themosis\Support\Section
pour la gestion des sections dans la metabox.
Metabox::make('properties', 'jobs')->setTitle('Paramètres du job')
->add(new Section('general','General', [
Field::text('company'),
Field::text('url', ['rules' => 'required|url', 'placeholder' => 'Votre url', 'info' => 'test'])
]))->add(new Section('details', 'Details', [
Field::text('source'),
Field::text('date')
]))->set();
Options disponibles pour la classe Field:
label, attributes, info, placeholder, rules, show_in_rest
Création du controller avec la commande php console make:controller JobsController
On ajoute au routeur Route::get('singular', ['jobs', 'uses' => 'JobsController@single'])->name('jobs.single');
On ajouter à notre controlleur les informations
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class JobsController extends Controller{
public function single(\WP_Post $post){
return view('jobs.single', compact('post'));
}
}
Enfin, on termine par la création de la vue: single.php
<div class="flex-grow flex flex-col bg-white border-b sm:rounded sm:border shadow overflow-hidden">
@isset($header)
<div class="border-b">
<div class="flex justify-between px-4"> {{ $header }}</div>
</div>
@endisset
{{ $slot }}
@isset($footer)
<div class="px-4 py-10 text-grey">{{ $footer }}</div>
@endisset
</div>
@extends('app')
@section('content')
<section class="job">
<div class="job__header py-20 border-b">
<div class="flex justify-between container mx-auto">
<h1>{{ $post->post_title }}</h1>
<p class="text-grey-dark">Publié le {{ Loop::date() }}</p>
</div>
</div>
<div class="job__content">
<div class="container mx-auto flex py-20">
<div class="job__description w-2/3 text-grey-darkest text-xl leading-normal mr-32">
{!! $post->post_content !!}
</div>
<div class="job__details w-1/3">
@component('components.card')
@slot('header')
<h3 class="text-blue-dark py-5 font-medium text-lg">A propos de cet emploi</h3>
@endslot
<div class="flex-grow flex flex-col text-grey-darker border-b">
<div class="w-full px-6 py-6 border-b">{{ get_post_meta($post->ID, 'th_company', true) }}</div>
<div class="w-full px-6 py-6 border-b">{{ get_post_meta($post->ID, 'th_source', true) }}</div>
<div class="w-full px-6 py-6">{{ get_post_meta($post->ID, 'th_date', true) }}</div>
</div>
@slot('footer')
<a href="{{ get_post_meta($post->ID, 'th_url', true) }}" target="_blank" class="no-underline bg-blue hover:bg-blue-dark text-white border border-blue-dark rounded px-6 py-4">Appliquer</button>
<a href="" class="no-underline text-grey-dark ml-5">Partager cet offre</a>
@endslot
@endcomponent
</div>
</div>
</div>
</section>
@endsection
Dans le routeur, Route::get('/jobs', ['uses' => 'JobsController@index'])->name('jobs.index')
Dans le controlleur, on ajoute la méthode puis création du fichier blade
@extends('app')
@section('content')
<section class="jobs">
<div class="jobs__header py-20">
<div class="flex justify-between container mx-auto">
<h1>Tous les emplois <span class="underline font-black text-red-light">Laravel</span> autours de <span class="font-black text-red-light">Montréal</span></h1>
</div>
</div>
<div class="jobs__content mb-20 ">
@component('components.card')
<div class="flex-grow flex flex-col text-grey-darker">
@query(['post_type' => 'jobs', 'posts_per_page' => 10])
<div class="w-full flex px-6 py-6 border-b items-center">
<h3 class="mr-5 w-1/3 truncate">{{ Loop::title() }}</h3>
<p class="mr-20">Montréal, Canada</p>
<p>{{ get_post_meta(Loop::id(), 'th_company', true) }}</p>
<a href="{{ get_permalink(Loop::id()) }}" class="no-underline ml-auto text-grey">Plus de détails</a>
</div>
@endquery
</div>
@endcomponent
</div>
</section>
@endsection
Création du model avec la commande php console make:model Job
Notre modèle Eloquent fonctionne, mais nous devons filter les posts par type et ne pas récupérer les posts non publiés (brouillons)
Installation du package Corcel: composer require jgrossi/corcel
, puis configuration à la Laravel
On ajoute le provider dans le fichier app.php
des configurations: Corcel\Laravel\CorcelServiceProvider::class
Puis installation de nos vendors du package avec la commande php console vendor:publish
Désormais, les modèles doivent hérités de la classe Corcel\Model\Post
;
Dans le model, la propriété protected $postType = 'jobs';
permet de spécifier le custom post type que l'on souhaite utiliser.
Modification de la vue
@foreach($jobs as $job)
@component('components.card')
<div class="flex-grow flex flex-col text-grey-darker">
<div class="w-full flex px-6 py-6 border-b items-center">
<h3 class="mr-5 w-1/3 truncate">{{ $job->post_title }}</h3>
<p class="mr-20">Montréal, Canada</p>
<p>{{ $job->meta->th_company }}</p>
<a href="{{ get_permalink($job->ID) }}" class="no-underline ml-auto text-grey">Plus de détails</a>
</div>
</div>
@endcomponent
@endforeach
Création de notre commande php console make:command DownloadJobsFromIndeedCommand
Installation de notre librairie CURL: composer require ixudra/curl
$jobs = (new CurlService)->to('https://api.indeed.com/ads/apisearch')->withData(['publisher' => '1303284387458115', 'co' => 'CA', 'q' => 'laravel', 'l' => 'Montréal', 'format' => 'json'])->asJson()->get();
foreach($jobs->results as $job):
$jobInstance = Job::create(['post_title' => $job->jobtitle, 'post_content' => $job->snippet, 'post_excerpt' => $job->snippet, 'to_ping' => false, 'pinged' => '', 'post_content_filtered' => '', 'post_type' => 'jobs']);
$jobInstance->saveMeta([
'th_company' => $job->company,
'th_url' => $job->url,
'th_source' => $job->source,
'th_date' => $job->date,
]);
endforeach;
On ajoutera ensuite notre commande dans le task scheduler de Themosis
#Création d'un template pour la page s'abonner par courriel
php console make:migration createJobSubscriptionsTable
php console make:model Models/JobSubscription
On ajoute à nos dépendances JS Axios pour appeller en AJAX facilement. `npm install axios``
Axios.get(themosis.ajaxurl, {params: {action: 'subscribe_job', email: 'jb@deviz.net'}});
Et le listener dans une class Hook
use App\Models\JobSubscription;
Ajax::listen('subscribe_job', function($params){
JobSubscription::create(['email' => request()->get('email')]);
});
Puis ajouter la proprité $fillable = ['email'] au model JobSubscription
php console make:command SendJobsNotificationsCommand
$subscriptions = JobSubscription::get();
foreach($subscriptions as $subscription):
$jobs = Job::get();
Mail::to($subscription->email)->send(new JobsNotificationMail($jobs));
endforeach;
php console make:mail JobsNotificationMail
@component('mail::message')
<h1>Dernières offres publiées</h1>
@component('mail::table')
| Nom | Compagnie |
| ---------------------------- |-----------------------------------|
@foreach($jobs as $job)
| {{ $job->post_title }} | {{ $job->meta->th_company }} |
@endforeach
@endcomponent
@component('mail::button', ['url' => 'http://larajobs.local'])
Voir les nouveaux jobs disponibles
@endcomponent
@endcomponent