Code for making a Shortcut tool for your App

Posted: 2015-03-29 20:43:12

This allows the user to easily make shortcuts to urls they are on and give them names.

image 1

video

The Migration

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateShortcutsTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('shortcuts', function(Blueprint $table)
        {
            $table->string('id', 36)->primary();
            $table->string('url');
            $table->string('name');
            $table->string('user_id', 36);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('shortcuts');
    }

}

The Model

You will see me using scopes as I wanted to make a really simple POC

<?php
/**
 * Created by PhpStorm.
 * User: alfrednutile
 * Date: 3/28/15
 * Time: 8:28 PM
 */

namespace BehatEditor\Models;


use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class Shortcut extends BaseModel {

    public $incrementing = false;

    public static $rulesCreate = [
        'name' => 'required|min:3',
        'url'  => 'required'
    ];

    protected $fillable = [
        "id",
        "url",
        "name",
        "user_id"
    ];

    public function user()
    {
        return $this->belongsTo('BehatEditor\User');
    }

    public function scopeGetAllForCurrentUser($query)
    {
        return $query->where('user_id', Auth::user()->id)->get();
    }

    public function scopeDeleteForUser($query, $shortcut_id)
    {
        try
        {
            return ($results = $query->where('id', $shortcut_id)->where('user_id', Auth::user()->id)->first()) ? $results->delete() : false;
        }
        catch(\Exception $e)
        {
            throw new \Exception(sprintf("Could not delete the shortcut %s", $shortcut_id));
        }
    }

    public function scopeCreateForUser($query, $input)
    {
        try
        {
            $uuid = $this->generateNewId()->toString();
            $this->create(
                [
                    'id'        => (isset($input['id'])) ? $input['id'] : $uuid,
                    'url'       => $input['url'],
                    'name'      => $input['name'],
                    'user_id'   => Auth::user()->id
                ]
            );
            return $uuid;
        }
        catch(\Exception $e)
        {
            $this->throw_and_log_error(sprintf("Error making shortcut %s", $e->getMessage()));
        }
    }

}

The Controller

Typically this is a no no too much logic in the controller.

<?php
/**
 * Created by PhpStorm.
 * User: alfrednutile
 * Date: 3/28/15
 * Time: 8:45 PM
 */

namespace BehatEditor\Http\Controllers;


use AlfredNutileInc\CoreApp\BaseController;
use BehatEditor\Models\Shortcut;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Validator;

class ShortcutsController extends BaseController {


    public function getUserShortCuts()
    {
        try
        {

            $results['shortcuts'] = Shortcut::getAllForCurrentUser();
            return Response::json($this->responseServices->respond($results, "Loaded Shortcuts"), 200);
        }
        catch(\Exception $e) {
            return Response::json($this->responseServices->respond($e->getMessage(), sprintf("Error Getting Shortcuts Failed %s", $e->getMessage())), 422);
        }
    }

    public function deleteUserShortCut($shortcut_id)
    {
        try
        {
            $results = Shortcut::deleteForUser($shortcut_id);
            return Response::json($this->responseServices->respond($results, "Deleted Shortcut"), 200);
        }
        catch(\Exception $e) {
            return Response::json($this->responseServices->respond($e->getMessage(), sprintf("Error Getting Shortcuts Failed %s", $e->getMessage())), 422);
        }
    }

    public function postShortcut()
    {
        try
        {
            $input = $this->getInput();
            $validator = Validator::make($input, Shortcut::$rulesCreate);
            if(!$validator->passes()) {
                return Response::json($this->responseServices->respond($validator->messages(), "Validation failed"), 422);
            }
        }
        catch(\Exception $e)
        {
            Log::debug(sprintf("Error making shortcut during validaiton %s", $e->getMessage()));
            return Response::json($this->responseServices->respond([], "Creation Error"), 500);
        }

        try
        {
            $results = Shortcut::createForUser($input);
            return Response::json($this->responseServices->respond($results, "Created Shortcut"), 200);
        }
        catch(\Exception $e) {
            return Response::json($this->responseServices->respond($e->getMessage(), sprintf("Error Getting Shortcuts Failed %s", $e->getMessage())), 422);
        }
    }

}

Then for the nav area

<li class="shortcut-form">
                <form class="navbar-form navbar-left" role="shortcuts">
                    <div class="input-group">
                            <input
                                    placeholder="shortcut name"
                                    type="text"
                                    class="form-control input-sm"
                                    ng-model="main.shortcut_new.name">
                            <span class="input-group-btn">
                                <button
                                        ng-disabled="!main.shortcut_new.name"
                                        type="button"
                                        class="btn btn-default"
                                        ng-click="main.addShortCut()">
                                    <i class="fa fa-plus-circle"></i>
                                </button>
                            </span>
                    </div>
                </form>
            </li>
            <li class="dropdown">
                <a class="dropdown-toggle" href="#">
                    <i class="fa fa-thumb-tack"></i> 
                    Your Shortcuts<span class="caret"></span>
                </a>
                <ul class="dropdown-menu dropdown-messages">
                    <li>
                        <input
                                class="form-control"
                                type="text"
                                autofocus="{{ form_focus == 'nav' }}"
                                placeholder="Click Tab to enter mouse into input and search"
                                data-ng-model="search_shortcuts"
                                >
                    </li>
                    <li class="divider"></li>
                    <li ng-repeat="shortcut in main.shortcuts | filter:search_shortcuts">
                        <div class="dropdown-messages-box">
                            <div>
                                <span ng-bind-html="shortcut.shortcut"></span>
                                <a class="pull-right" href="#" ng-click="main.deleteShortcut(shortcut.id)">
                                    <i class="fa fa-trash"></i>
                                </a>
                            </div>
                        </div>
                        <div class="divider"></div>
                    </li>
                </ul>
            </li>

The Angular

We have a MainController for initial app setup then after that ui-router has controllers.

Also you can get the shortcuts via a http request on page load. I just inject the initial load into the blade render. But either is fine really.

# controller.js

        function addShortCut()
        {
            vm.shortcut_new.url = $location.url();
            vm.ShortcutsService.create(vm.shortcut_new, vm.callbackCreateShortcutSuccess, vm.callbackShortcutError);
        }

        function loadShortcuts()
        {
            vm.shortcuts = [];
            angular.forEach(vm.ENV.shortcuts, function(v,i){
                var link = vm.makeLink(v);
                vm.shortcuts.push( { "id": v.id, "shortcut": link } );
            });
        }

        function deleteShortcut(id)
        {
            vm.shortcut_to_delete = id;
            vm.ShortcutsService.deleteShortcut(id, vm.callbackShortcutSuccess, vm.callbackShortcutError);
        }

        function callbackShortcutSuccess(response)
        {
            vm._.remove(vm.shortcuts, function(s) {
               return s.id == vm.shortcut_to_delete;
            });
            vm.toaster.pop("success", "Success updating shortcut");
        }

        function callbackCreateShortcutSuccess(response)
        {
            vm.shortcut_new.id = response.data;
            var link = vm.makeLink(vm.shortcut_new);
            vm.shortcuts.push( { "id": vm.shortcut_new.id, "shortcut": link } );
            vm.shortcut_new = {};
            vm.toaster.pop('info', "Success creating shortcut");
        }

        function makeLink(shortcut)
        {
            return "<a href='/behat#" + shortcut.url + "'>" + shortcut.name + "</a>";
        }

        function callbackShortcutError(response)
        {
            vm.toaster.pop("error", "Error updating your shortcut");
        }

The Behat API Tests

@api
Feature: Shortcuts
  Shortcuts for quick access
  As an authenticated user
  So I can make and use shortcuts to get from place to place

  Background: Login
    Given I do basic auth on behat

  Scenario: Get My Shortcuts
    When I request "GET /api/v1/shortcuts"
    Then I get a "200" response
    And scope into the "data.shortcuts.0" property
    And the properties exist:
    """
    url
    user_id
    """

  Scenario: Delete My Shortcuts
    When I request "DELETE /api/v1/shortcuts/mock-shortcut-5"
    Then I get a "200" response

  Scenario: Can Create a Shortcut
    Given I reseed the database
    Given I have the payload:
    """
      { "data":
        {
           "name": "New ShortCut",
           "url": "/dashboard"
         }
       }
    """
    When I request "POST /api/v1/shortcuts"
    Then I get a "200" response