Twitter Marketing Is Huge
Well for my platform it is anyway, whenever I create a new app or website Twitter is the first social network that I turn to. It’s fast, the analytics are on point, and it’s easy to use, my only issue is that I am busy coding and whatnot to Tweet as much as I would like or need to in order to optimize my reach. This leaves me with two options:
- Pay someone to manage my account for me
- Learn the Twitter API and write my own automation program to manage it myself
The idea of paying another person to manage my social media just made my stomach and my wallet hurt so the OBVIOUS choice is to use the API. Now let me tell you thisn API is DENSE there is a lot you can do with it (click here for the docs) so I will only focus on one aspect in this tutorial, sending posts. The first basic application that I wrote was a simple post scheduler, it allows me to create as many posts as I want, set a date-time to post at, then a Laravel job executes at the desired time, then deletes the entry from the database. This basic application has saved me tremendous time because now I can just schedule all my important tweets weeks or months early!
Registering Your Twitter App
Before you can do any actual coding you need to register your app on Twitter, head over to the developer portal and create your app. The home page will show a list of your Twitter apps, if this is your first time then you won’t have any apps, click on create new app.
Fill out all the mandatory fields then proceed you should see a screen that shows you your Consume Key and Consumer Secret. Copy those down and also create some access tokens. These access tokens are for writing Twitter apps that only interact with your OWN account. Copy down the access token and the access token secret and that’s all you have to do. Now let’s code!
App Components
The main page will be a Vue.js app that allows you to add new tweets, and an area below to edit and delete them. In a traditional SPA manner the app will interact with an API for all it’s calls. The backend will consist of one model (Tweet), one controller (TweetController), and one Job ProcessTweet, pretty simple stuff let’s begin. Start by creating your Laravel application.
laravel new tweeter && cd tweeter && php artisan make:auth
This app only has one composer dependency add it
composer require thujohn/twitter
In the config/app.php make sure to add the following provider and alias
'providers' => [
Thujohn\Twitter\TwitterServiceProvider::class,
]
'aliases' => [
'Twitter' => Thujohn\Twitter\Facades\Twitter::class,
]
Lastly run the following command to import the twitter config/ttwitter.php file and modify inserting your Twitter app credentials
php artisan vendor:publish --provider="Thujohn\Twitter\TwitterServiceProvider"
Tweet Model
Now create the Tweet model along with it’s migration
php artisan make:model -m Tweet
The Tweet model only has two attributes a string that holds the content that is limited to 140 characters and a timestamp for when you want the tweet to be published.
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTweetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tweets', function (Blueprint $table) {
$table->increments('id');
$table->string('content',140);
$table->timestamp('publish_timestamp');
$table->timestamps();
});
}/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tweets');
}
}
The Tweet model is pretty bare bones
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tweet extends Model
{
//
public $fillable = [
'content',
'publish_timestamp'
];
}
Go ahead and run your migrations we are done with models, now to make our RESTful controller.
RESTful Controller and Routes
Create a resource controller for our Tweet model
php artisan make:controller --resource TweetController
The controller is basic I purposely left out validation and such as a homework exercise for you, but the gist is basic CRUD operations that return JSON
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Tweet;
use App\Jobs\ProcessTweet;
class TweetController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
return response()->json(Tweet::all());
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//create the tweet
$tweet = Tweet::Create([
'content' => $request->content,
'publish_timestamp' => \Carbon\Carbon::parse($request->publish_timestamp)
]);
//Add tweet to the queue
ProcessTweet::dispatch($tweet)->delay(\Carbon\Carbon::parse($request->publish_timestamp,'America/New_York')->diffInSeconds(\Carbon\Carbon::now('America/New_York')));
//return json
return response()->json($tweet);
}/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
return response()->json(Tweet::findOrFail($id));
}/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
$tweet = Tweet::findOrFail($id);
$tweet->fill([
'content' => $request->content,
'publish_timestamp' => \Carbon\Carbon::parse($request->publish_timestamp)
]);
//return json
return response()->json($tweet);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
Tweet::destroy($id);
}
}
Notice the ProcessTweet job? We will implement that next but for now open up the routes/api.php file and add the following resource route
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::resource('/tweet','TweetController');
Now our Vue application (patience it’s coming) can interact with our backend. Now let’s add our ProcessTweet job and we will be finished on the backend.
ProcessTweet Job
In order to schedule the tweets to post at any given time you must utilize Laravel Jobs this will allow you to schedule your events to fire at a certain point in time. If you aren’t familiar with jobs and queues please look at my previous post because I will not be covering setting up queues in this tutorial, otherwise create a new job
php artisan make:job ProcessTweet
In the handle method we simply call the Twitter API and send the tweet then delete it from the database
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Twitter;
class ProcessTweet implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tweet;
/** * Create a new job instance.
*
* @return void
*/
public function __construct(\App\Tweet $tweet)
{
//
$this->tweet = $tweet;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//post the tweet
Twitter::postTweet(['status' => $this->tweet->content, 'format' => 'json']);
//delete the tweet from database
\App\Tweet::destroy($this->tweet->id);
}
}
Your backend is now functioning run your queue worker and test around with it or continue to creating the Vue application.
Getting The Front End Together
We need to add vue-resource to call our api so let’s add that first
npm install --save vue-resource
Also update the resources/js/app.js file
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
const resource = require('vue-resource');
Vue.use(resource);
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('tweet-component', require('./components/TweetComponent.vue'));
const app = new Vue({
el: '#app'
});
As you can see the example-component has been replaced with the tweet-component rename your ExampleComponent.vue to TweetComponent.vue and update it with the following contents
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Tweet Scheduler</div>
<div class="panel-body">
<div class="form-group">
<input class="form-control" placeholder="Content" v-model="newTweet.content">
</div>
<div class="form-group">
<input class="form-control" type="datetime-local" placeholder="Description" v-model="newTweet.publish_timestamp">
</div>
<div class="form-group">
<button class="btn btn-success" v-on:click="addTweet(newTweet)">Add Tweet</button>
</div>
<ul v-if="ready" class="list-group">
<li v-for="post in tweets" class="list-group-item clearfix">
{{post.content}}
<span class="pull-right button-group">
<button class=" btn btn-default" v-on:click="openEditTweet(post)">Edit</button>
<button class=" btn btn-danger" v-on:click="deleteTweet(post)">Delete</button>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div id="editModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">{{selectedTweet.content}}</h4>
</div>
<div class="modal-body">
<div class="form-group">
<input class="form-control" placeholder="Content" v-model="selectedTweet.content">
</div>
<div class="form-group">
<input class="form-control" type="datetime-local" placeholder="Publish At" v-model="selectedTweet.publish_timestamp">
</div>
<div class="form-group">
<button class="btn btn-info" v-on:click="editTweet(selectedTweet)">Edit Tweet</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data(){
return {
tweets: {},
newTweet:{
'content': '',
'publish_timestamp': ''
},
selectedTweet:{
'content': '',
'publish_timestamp': ''
},
ready: false
}
},
created(){
this.$http.get('/api/tweet').then(function(data){
this.tweets = data.data;
this.ready = true;
});
},
methods: {
addTweet: function(tweet){
this.$http.post('/api/tweet',tweet).then(function(data){
this.tweets.unshift({content:tweet.content,publish_timestamp:tweet.publish_timestamp});
});
},
editTweet: function(tweet){
this.$http.put('/api/tweet/'+tweet.id,this.selectedTweet).then(function(data){
let index = this.tweets.indexOf(tweet);
this.tweets[index] = tweet;
alert('Updated!');
$("#editModal").modal('hide');
});
},
openEditTweet: function(tweet){
console.log(tweet);
this.selectedTweet = tweet;
$("#editModal").modal({show: true});
},
deleteTweet: function(tweet){
this.$http.delete('/api/tweet/'+tweet.id).then(function(data){
let index = this.tweets.indexOf(tweet)
this.tweets.splice(index, 1);
});
}
}
}
</script>
Open up your resources/assets/sass/app.scss file and add the following fade transition
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0
}
Run your npm build script as well as your queue worker and start tweeting! You can get the full source code here, and as always please subscribe to my blog via email or push notifications, share, and leave your comment below! If you would like to add a real-time nodejs microservice to your app then read here!