When to use PHP vs NodeJS

By: Ryan Wong at

There has been a lot of blogs written about how php is bad or node is bad, but most of them are missing a lot of context.

Most of the time it seems like a marketing campaign for adoption or circle jerking to boost their own language/framework.

I have worked in both php and node environment’s and I just want make more people aware of when you should use what.

I will give real points not something written by someone who just did the hello world tutorial and claims to be an expert.

PHP

PHP is in a very establish and mature language people have heard about for decades. This also means that a lot of solutions
have been developed in this space that are ready to use.

Some good features of PHP

  • PHP gives you direct response once you hit the same button. No other language/framework give you this. This speeds up the feedback loop. No starting servers or other things
  • Establish library of packages in composer so you can find solutions for what you need
  • Has many establish open source projects that you can use and focus on your business (wordpress, drupal, etc.)
  • Easy to learn, do follow best practices that software engineers should know(classes, interfaces, etc.)
  • Very little ramp up time to setup the system to start coding
  • Lots of tutorials to almost anything you need
  • Still being upgraded and maintained
  • Huge community
  • can deploy to almost any VPS server for clients without much hassle
  • file sizes are very small

Some bad features of PHP

  • Since the community is so establish, people in third world countries are very familiar with php and they will compete with you for projects. This drives the price for PHP development and developers down.
  • Wordpress developers give php developers a bad name
  • Wordpress developers not being as knowledgable in software development practice
  • Wages are a lot lower than other languages
  • not setup in a way to support scaling
  • not designed to handle asynchronous. Need work arounds
  • Not designed for single page applications
  • Stigma by other people for being simplistic, easy, non developer work

When to use PHP

I think if you are doing work for clients that need establish software solutions, I would go for PHP ever time.
From a business point of view especially startups, PHP provides a lot of products off the shelf for you to focus more on
your business instead of trying to reinvent the wheel. I think from a developer point of view, you should try to move away
from PHP due to the depreciated salaries.

The most common argument you hear from other bloggers is that PHP is slow. But the real question you should as is

how much speed do you actually need on your site?

Does a CRM need to react to millions of request per second?

Does a CMS need to react to millions of request per second(if it does, you can always cache it in redis)?

Does a forum care about react to millions of request per second?

Does a Ecommerce solution need react to millions of request per second(maybe, but again you can cache it)?

Does a Blog need to react to millions of request per second?

Does a time tracking software need react to millions of request second?

Problems that need to be fast:

  • API servers (multiple devices hitting it like mobile, desktop, IoT)
  • front facing website
  • ad servers
  • real time services

Here’s a few example of solutions you should use PHP for:

  • blogs (I would use wordpress. Why try to reinvent the wheel to do the same thing?)
  • ecommerce (I would personally go shopify for small shop, it has better UX and more stuff done for you. But you could use magento, opencart and other establish systems)
  • creating an ad server (revive-adserver)
  • forum site (phpbb)
  • time tracking software (lots of time tracking solutions)
  • ERP solution (inoERP)
  • CMS (drupal)
  • CRM (sugarcrm, suitecrm)
  • helpdesk solution

Node

Node has come out in the past few years and has exploded in popularity. Node selling point is that it’s single thread execution,
speed, and one language for the system. It basically became what ruby on rails was years ago.

Some good features of Node

  • it has a very good package manager called npm
  • it has very simple to use and learn frameworks
  • it seperates frontend from backend code which is a new paradigm
  • it can handle 250,000 request per second with 1 server running
  • lots of tooling is created
  • if you know front end javascript, you can pick up node
  • supports asynchronous request
  • designed to help single page application
  • lots of people haven’t adapted it so salary are high still
  • very good for CRUD like solutions

Some bad features of Node

  • asynchronous request can create callback hell making the code less readable
  • lots javascript flaws start to show when you code in node which can make code very messy if you have a bad developer
  • not a lot of solutions created for you
  • huge file sizes fro project due to libraries (I’ve had 250MB)
  • isn’t as supported as PHP for VPS type servers which most clients use
  • not as easy to setup. Need to DIY a lot more

When to use Node

I think you should use Node if you need the speed. Some good examples are:

  • system needs to be very custom it
  • JSON management(CRUD)
  • API server
  • microservice architecture

Overall, I feel it depends whos asking the question. If your developer, Node is more marketable than PHP. If your business
and need to get stuff done use PHP. If you need a very custom solution and have the people to do it use Node. Even tough I
said PHP wasn’t design for certain features, you can always work around it.

Angular File Structure

By: Ryan Wong at

There’s is lots of different ways to organize your angular project.

If your a small project, it doesn’t matter how you do it as long as it gets done fast.

Some people do feature based and some do a traditional MVC folder structure. I will show you what I do after.

Feature based

Feature based means htat you organize your folders by feature in your project. So Home page, Admin, Search and etc.

Advantage

  • it’s very clear how to find your code

Disadvantage

  • once project gets really big, there will be 100 folders at your base level directory and you start getting confuse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/app
main.js
/features
/home
HomeController.js
home.html
home.js
home.scss
/search
SearchController.js
search.html
search.js
search.scss
... //eventually there will be 100s of folders
/components
/modal
ModalDirective.js
/navbar
NavBarDirective.js
...

MVC

People who are familiar with MVC style development will understand this very well and be right at home

Advantage

  • very clear organization where code is (service is here, controller is here)

Disadvantage

  • gets really bulky very fast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/app
/model
UserFactory.js
...
/services
UserService.js
...
/controllers
/home
HomeController.js
/search
SearchController.js
... (gets bulky)
/views
/home
home.html
/search
search.html
... (gets bulky)
/scss
home.scss
search.scss
...

Module Based

I group all code that are in one module together and create common folder for things everyone shares.
I also use browserify to concatenate all files into 1 file.
The code is broken down into classes so it is easy to migrate to angular 2.
It is a mix of both MVC style and feature based.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/app
entry.js //load in all modules
/scss
main.scss //concatenate all non underscore scss
/marketing
_home.scss
marketing.scss // concatenate all _ scss
...

/common
/model
/services
index.js //bind all classes in services into index.js
/http
HttpService.js //Class
/utility
Utility.js //Class
index.js //bind all classes in utility into index.js
/uicomponents
index.js //bind all classes in uicomponent into index.js
/spinner
Spinner.js //Class
...
/marketing
index.js //bind all modules into index.js
routes.js //array of all routes
/models
UserModel.js //Class
index.js //bind all classes in model into index.js
/services
index.js //bind all classes in services into index.js
/user
UserService.js //Class
...
/views
/directives
index.js //bind all classes in directives into index.js
/somewidget
SomeWidget.js //Class
/modals
index.js //bind all classes in modals into index.js
/somemodal
SomeModal.js //class
/pages
index.js //bind all classes in page into index.js
/home
HomeController.js
home.html
/admin
// same as marketing
/member

So what I did was make every Controller, directive, service into a javascript class which you can DI angular properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
var SomeController = function ($scope, $state, $log) {
vm._scope = $scope;
vm._state = $state;
vm._log = $log;
};

SomeController.prototype = {
somefunction: (){
//do something
}
};

module.exports = SomeController;

Then in your index.js file in the upper level folder

1
2
3
4
var SomeController = require('app/marketing/views/pages/someController/SomeController');

angular.module('marketing.pages', []).run(function(){})
.controller('someController',['$scope', '$state', '$log', SomeController]);

By doing this, we remove our codes dependancy on angular making it reusable by in the future. $scope can be injected in
as something else. You can also test this class individually now without needing angular.

The index.js file in the high level module would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var routes = require('app/marketing/route');

require('app/marketing/service');
require('app/marketing/view/modals');
require('app/marketing/view/directives');
require('app/marketing/view/pages');
angular.module('marketing', [
'ui.router',
'marketing.services',
'marketing.modals',
'marketing.pages',
'marketing.directives',
]).run([function(){}])
.config(function ($stateProvider) {
for(var i = 0; i < routes.length; i++){
$stateProvider.state(routes[i].stateName, routes[i]);
}
});

The routes file would look like this:

1
2
3
4
5
6
7
8
9
module.exports = [
{
stateName: 'app.marketing',
url: '/',
templateUrl: 'app/marketing/views/pages/home/home.html',
controller: 'someController',
controllerAs: 'vm',
}
]

The entry.js file would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require('core');
require('marketing');
require('admin');
require('member');

var app = angular.module('app', [
'ui.router',
'ui.bootstrap',
'ngSanitize',
'app.templates',
'core',
'marketing',
'admin',
'member'
])
.config(['$stateProvider',
'$urlRouterProvider',
'$locationProvider',
function ($stateProvider,
$urlRouterProvider,
$locationProvider)
{

$locationProvider.html5Mode({
enabled: true,
requireBase: false
}).run(function(){});

twitter-widget

By: Ryan Wong at

Here’s how you create a twitter widget.

Controller:

1
TwitterService.load();

Directive:

1
2
3
4
5
6
7
8
9
angular('app').factory('TwitterService', ['$timeout', function ($timeout) {
return {
load: function () {
$timeout(function() {
$.ajax({ url: 'http://platform.twitter.com/widgets.js', dataType: 'script', cache:true});
}, 1000);
}
}
}]);

HTML:

1
2
<a class="twitter-timeline" href="https://twitter.com/company" data-widget-id="411817353240772608">Tweets by @company</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

Useful Gulp Task

By: Ryan Wong at

Here are some very useful gulp task.

SASS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css');

gulp.task('sass', function(done) {
gulp.src(paths.sassSrc)
.pipe(sass())
.on('error', sass.logError)
.pipe(minifyCss({
keepSpecialComments: 0
}))
.pipe(gulp.dest(paths.sassDest))
.on('end', done);
});

gulp.task('sass:dev', function(done) {
gulp.src(paths.sassSrc)
.pipe(sass())
.on('error', sass.logError)
.pipe(gulp.dest(paths.sassDest))
.on('end', done);
});

gulp.task('sass:prod', function(done) {
gulp.src(paths.sassSrc)
.pipe(sass())
.on('error', sass.logError)
.pipe(minifyCss({
keepSpecialComments: 0
}))
.pipe(rename({ extname: '.min.css' }))
.pipe(gulp.dest(paths.sassProdDest))
.on('end', done);
});

Templating:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var templateCache = require('gulp-angular-templatecache');

gulp.task('template:dev', function () {
return gulp.src(paths.templateSrc)
.pipe(templateCache({
standalone: true,
module: 'videa.templates'
}))
.pipe(gulp.dest(paths.bundleDest));
});

gulp.task('template:prod', function(){
return gulp.src(paths.templateSrc)
.pipe(templateCache({
standalone: true,
module: 'videa.templates'
}))
.pipe(gulp.dest(paths.jsProdDest));
});

Packaging all vendor bower components to one file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
var ngAnnotate = require('browserify-ngannotate');
var ngAnnotateAngular = require('gulp-ng-annotate');
var mainBowerFiles = require('main-bower-files');
var print = require('gulp-print');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var filter = require('gulp-filter');

gulp.task('package-bower-js:dev', function(){

//Package Vendor JS Files
gulp.src(mainBowerFiles({
overrides: {
jquery: {
ignore: true
},
bootstrap: {
ignore: true
}
}
}))
.pipe(filter(['**/*.js','*.js']))
//.pipe(print())
.pipe(concat('vendor.js'))
//.pipe(plugins.uglify())
.pipe(gulp.dest(paths.bundleDest));
});

gulp.task('package-bower-css:dev', function(){

//Package Vendor CSS Files
gulp.src(mainBowerFiles({
overrides: {
bootstrap: {
main: [
'./dist/css/bootstrap.min.css'
]
}
}
}))
.pipe(filter(['*.css', '*.scss','**/*.css']))
.pipe(concat('vendor.css'))
.pipe(gulp.dest(paths.cssDest));
});

gulp.task('package-bower-fonts:dev', function(){

//Package Vendor Font Files
gulp.src(mainBowerFiles({
overrides: {
bootstrap: {
main: [
'./fonts/*'
]
}
}
}))

.pipe(filter(['**/*.eot', '**/*.svg', '**/*.ttf', '**/*.woff', '**/*.woff2',
'./*.eot', '*.svg', '*.ttf', '*.woff', '*.woff2']))
.pipe(gulp.dest(paths.fontDest));
});

gulp.task('package-bower-js:prod', function(){

//Package Vendor JS Files
gulp.src(mainBowerFiles({
overrides: {
jquery: {
ignore: true
},
bootstrap: {
ignore: true
}
}
}))
.pipe(filter(['**/*.js','*.js']))
.pipe(concat(paths.vendorProdSrc))
.pipe(ngAnnotateAngular())
.pipe(streamify(uglify({
output: {
comments: false
},
mangle: true
})))
.pipe(gulp.dest(paths.jsProdDest));
});

gulp.task('package-bower-css:prod', function(){

//Package Vendor CSS Files
gulp.src(mainBowerFiles({
overrides: {
bootstrap: {
main: [
'./dist/css/bootstrap.min.css'
]
}
}
}))
.pipe(filter(['*.css', '*.scss','**/*.css']))
// .pipe(print())
.pipe(concat(paths.vendorCSSProdSrc))
.pipe(minify())
.pipe(gulp.dest(paths.sassProdDest));
});

gulp.task('package-bower-fonts:prod', function(){

//Package Vendor Font Files
gulp.src(mainBowerFiles({
overrides: {
bootstrap: {
main: [
'./fonts/*'
]
}
}
}))

.pipe(filter(['**/*.eot', '**/*.svg', '**/*.ttf', '**/*.woff', '**/*.woff2',
'./*.eot', '*.svg', '*.ttf', '*.woff', '*.woff2']))
.pipe(gulp.dest(paths.fontProdDest));
});
```

Our bower.js
```js

{
"name": "hahaha",
"version": "1.1.38",
"description": "hahaha",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"font-awesome": "~4.3.0",
"animate.css": "~3.3.0",
"underscore": "~1.8.3",
"angular": "~1.4.10",
"angular-animate": "~1.4.9",
"angular-breadcrumb": "~0.4.0",
"angular-cookies": "~1.4.4",
"angular-ui-router": "~0.2.14",
"angular-permission": "~0.3.1",
"ngstorage": "~0.3.3",
"angular-bootstrap": "~1.1.2",
"angular-ui-utils": "~3.0.0",
"ng-file-upload": "~5.0.9",
"angular-loader": "~1.4.4",
"angular-messages": "~1.4.4",
"angular-resource": "~1.4.4",
"angular-route": "~1.4.4",
"angular-sanitize": "~1.4.4",
"angular-touch": "~1.4.4",
"angular-loading": "~0.1.4",
"angularjs-slider": "~2.1.0",
"tinycolor": "~1.3.0",
"ut-angular-toast": "*",
"ui-select": "angular-ui-select#~0.13.2",
"bootstrap": "3.3.0",
"angular-color-picker": "angularjs-color-picker#~0.8.1",
"angular-ui-ace": "bower",
"html5-sortable-angularjs": "*"
},
"resolutions": {
"angular": "1.4.10"
}
}

Browserify:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
var browserify = require('browserify');
var vinylSource = require('vinyl-source-stream');
var ngAnnotate = require('browserify-ngannotate');
var ngAnnotateAngular = require('gulp-ng-annotate');
var streamify = require('gulp-streamify');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');

gulp.task('browserify:dev', function() {
return browserify(paths.entry, {
transform: [ngAnnotate],
debug: true,
cache: {}, packageCache: {}, fullPaths: true,
paths: [paths.browserifyAliasPath]
})
.bundle()
.on('error', function(message){
console.log(message);
})
.pipe(vinylSource(paths.bundleSrc))
.pipe(gulp.dest(paths.bundleDest));
});

gulp.task('browserify:prod', function () {
var bundleStream = browserify(paths.entry, {
transform: [ngAnnotate],
paths: [paths.browserifyAliasPath],
debug: true,
cache: {}, packageCache: {}, fullPaths: true
})
.bundle();

bundleStream
.pipe(vinylSource(paths.bundleSrc))
.pipe(streamify(uglify({
output: {
comments: false
},
mangle: false
})))
.pipe(rename({ extname: '.min.js' }))
.pipe(gulp.dest(paths.jsProdDest));
});

Starting server

1
2
3
4
5
6
var gls = require('gulp-live-server');

gulp.task('server', function() {
var server = gls('bin/www', {env: {NODE_ENV: 'development'}}, false);
server.start();
});

Compress

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var zip = require('gulp-zip');

Date.prototype.YYYYMMDDHHMMSS = function () {
var yyyy = this.getFullYear().toString();
var MM = pad(this.getMonth() + 1,2);
var dd = pad(this.getDate(), 2);
var hh = pad(this.getHours(), 2);
var mm = pad(this.getMinutes(), 2)
var ss = pad(this.getSeconds(), 2)
return yyyy + MM + dd+ hh + mm + ss;
};
gulp.task('compress', function () {
return gulp.src([
'package.json',
'README.txt'], {base: "."})
.pipe(zip('V' + new Date().YYYYMMDDHHMMSS() + '.zip'))
.pipe(gulp.dest('dist'));
});

Typescript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var sourcemaps = require('gulp-sourcemaps');
var ts = require('gulp-typescript');

gulp.task('typescript:backend', function() {
return gulp.src(paths.typescriptBackendSrc, { base: "." })
.pipe(sourcemaps.init())
.pipe(ts({
noExternalResolve: false,
noImplicitAny: false,
noLib: false,
module: 'commonjs',
target: 'ES5',
removeComments: true,
moduleResolution : "classic"
}))
.pipe(sourcemaps.write())
.pipe(gulp.dest('.'));
});

learning to learn

By: Ryan Wong at

How do you remember what you read?

  • recognition
    It’s also what happens when you’re reading. To read this text you must individually recognize each of the words I’ve written.
  • recall
    It’s the ability to pull up the answer to a question, without looking at it.

The problem is that if you want recallable memory, practicing recognition doesn’t help very much.

The solution is Question Book Method.

Except, don’t take notes which summarize the main points you want to recall. Instead, take notes which ask questions.

Which matters more for how much you retain: the method you use or your motivation to learn something?

Motivation, in the form of knowing the information was important and would be tested later, had no impact on how well the subjects did on the later test.

Students who did shallower processing only remembered half as many words as those who used deeper processing. The result was the same regardless of whether they were trying to remember the words or not.

Transcript Script is the typical note-taking approach is to copy down everything that is being said, in the way that the speaker says it. The problem with transcription style notes is that copying down exactly what is being said doesn’t require deep processing.

When you’re no longer allowed to use the exact phrases the lecturer uses you have to start really thinking about what is being said in order to translate it into your own thoughts.

Test Driven Learning

That means writing/find a (mock) final exam, before you ever attend a single lecture or open your textbook for the course.

It means doing practice problems before reading the text. It means attempting to speak a language before you’ve learned any words. It means trying to apply a skill before you’ve practiced it.

You can make a mental assessment of how the knowledge needs to be used before you take the class. Then, once you’re in the class, you’re already primed to think about the knowledge in the way that will help you retake the test again.

For practical skills you intend to use, for example, you can put yourself in a usage situation before you’ve studied anything yet.

How to get efficent study time

Set time aside to study.

Don’t study in evenings.

If you don’t schedule your off hours, your brain will do it for you—by procrastinating, getting distracted and losing focus.

You need to treat your learning time like it is—a scarce resource that must be used efficiently. If you treat it as an unending obligation, you’ll actively resist working when you start.

The key is to pick an activity as a break that is more boring than actually studying. A break shouldn’t be fun, it should be relaxing.

Some of my favorite mental breaks would be going for a short walk, drinking a glass of water, sitting quietly or taking a 10-minute nap

If your break is surfing online, texting or chatting with a friend, you’ll struggle to pull yourself back.

The key distinction that causes some of your skills to deteriorate while others last a lifetime

Students who scored A’s in algebra did remember more on later tests than the B and C students. But they forgot at exactly the same rate.

Overlearning is practicing something you’re already so good at that improvements aren’t measurable anymore. Overlearning may no longer produce measurable improvements in ability, but it does protect against subsequent loss of skill.

What you overlearn you won’t forget

ngSrc path resolves to a 404, angular directive to fallback to as default

By: Ryan Wong at

Here’s the solution to 404 images.

HTML:

1
<img ng-src="{{something}}" err-src="/assets/images/defaultLogo.png" class="max-width"/>

Directive:

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';

angular.module('app').directive('errSrc', function() {
return {
link: function(scope, element, attrs) {
element.bind('error', function() {
if (attrs.src != attrs.errSrc) {
attrs.$set('src', attrs.errSrc);
}
});
}
}
});

prevent-adblock

By: Ryan Wong at

This is the simplist approach to stop adblock.

HTML:

1
2
<script>var isAdBlockActive=true;</script>
<script src="ads.js"></script>

ads.js

1
var isAdBlockActive=false;

Then in your controller logic do :

1
2
3
if (isAdBlockActive) {
adBlock = true;
}

handling google and facebook bots

By: Ryan Wong at

This is how you handle the google bot and facebook bot.

Inside of where you load middleware

1
2
var useragent = require('express-useragent');
app.use(useragent.express());

Inside your main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
'use strict';

var url = require('url');
var path = require('path')
var childProcess = require('child_process')
var phantomjs = require('phantomjs')
var binPath = phantomjs.path;
var botService = path.join(__dirname, '../botservice.js');
var config = require('../config/environment');

function fullUrl(req) {
return req.protocol + "://" + (config.ip || 'localhost') + ":" + config.port + req.originalUrl;
}

module.exports = function(app){
app.route('/*').get(function(req, res) {
if(req.useragent && req.useragent.isBot){
var childArgs = [botService, fullUrl(req), "--load-images=false"];
childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) {
if(!err) {
res.send(stdout);
}
});
} else {
res.sendfile(app.get('appPath') + '/index.html');
}
});
};

botservice.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var page = require('webpage').create(),
system = require('system'),
url, apiURL;

if (system.args.length === 1) {
console.log('No URL Found');
phantom.exit();
}

url = system.args[1];


var resourceWait = 300,
maxRenderWait = 8000,
count = 0,
forcedRenderTimeout,
renderTimeout;

page.onResourceRequested = function (req, controller) {
count += 1;
clearTimeout(renderTimeout);
};

page.onResourceReceived = function (res) {
if (!res.stage || res.stage === 'end') {
count -= 1;
if (count === 0) {
renderTimeout = setTimeout(doRender, resourceWait);
}
}
};

function doRender() {
console.log(page.content);
setTimeout(function(){
phantom.exit();
}, 0);
};

page.open(url, function (status) {
if (status !== "success") {
console.log("Unable to access network");
phantom.exit(-1);
} else {
forcedRenderTimeout = setTimeout(function () {
doRender();
}, maxRenderWait);
}
});

In angular, this is how you can reset the metadata

entry.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
angular.module('app').service('PageTitle', function() {
var title,
defaultTitle;
title = defaultTitle = 'Some title';
return {
title: function() {return title; },
setTitle: function(newTitle) { title = newTitle + " - " + defaultTitle }
};
})

.service('MetaInformation', [function() {
var metaDescription,
ogDescription,
defaultMetaDescription,
metaKeywords,
ogImage,
defaultImage,
ogTitle,
defaultTitle;

metaDescription = ogDescription = defaultMetaDescription = 'default meta data description.';
metaKeywords = ' education, information';
ogImage = defaultImage = 'http://company.com/logo.png';
ogTitle = defaultTitle = 'network of tomorrow';
return {
metaDescription: function() { return metaDescription; },
metaKeywords: function() { return metaKeywords; },
ogImage: function() {return ogImage; },
ogTitle: function() {return ogTitle; },
ogDescription: function() {return ogDescription; },
url: function() {return document.location.href},
reset: function() {
metaDescription = defaultMetaDescription;
ogImage = defaultImage;
ogTitle = defaultTitle;
ogDescription = defaultMetaDescription;
},
setMetaDescription: function(newMetaDescription) {
metaDescription = newMetaDescription;
},
setOgImage: function(newOgImage) {
ogImage = newOgImage;
},
setOgTitle: function(newOgTitle) {
ogTitle = newOgTitle + " - " + defaultTitle;
},
setOgDescription: function(newOgDescription) {
ogDescription = newOgDescription;
metaDescription = newOgDescription;
},
appendMetaKeywords: function(newKeywords) {
for (var key in newKeywords) {
if (metaKeywords === '') {
metaKeywords += newKeywords[key].name;
} else {
metaKeywords += ', ' + newKeywords[key].name;
}
}
}
};
}]);


angular.module('app').run(function ($rootScope, $location, Auth, PageTitle, MetaInformation) {
$rootScope.PageTitle = PageTitle;
$rootScope.MetaInformation = MetaInformation;
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
$rootScope.MetaInformation.reset();
});
});

inside of a controller, you do the following

1
2
 $rootScope.PageTitle.setTitle('Something');
$rootScope.MetaInformation.setOgTitle('Something');

Laravel stripe get subscription date

By: Ryan Wong at

I’ve looked at alot of tutorials and couldn’t find one that can retrieve your subscription plans details like when subscription ends.
You must install laravel cashier first.

1
2
3
4
5
6
7
8
9
10
11
12
13
use Stripe_Customer as Customer;
public function getSubscriptionDetail($user)
{

Stripe::setApiVersion("2015-02-10");
Stripe::setApiKey(Config::get('services.stripe.secret'));
$customer = Customer::retrieve($user["stripe_id"]); //your customer stripe id
$subscription = $customer->subscriptions->retrieve($user["stripe_subscription"]); //stripe subscription column in your user table
return array(
'subscriptionPlan' => $subscription->plan->statement_descriptor,
'planEnd' => date('Y-m-d', $subscription->current_period_end),
'planPrice' => $subscription->plan->amount
);
}