Giter Site home page Giter Site logo

short-url's Introduction

Latest Version on Packagist Total Downloads PHP from Packagist GitHub license

Table of Contents

Overview

A Laravel package that can be used for adding shortened URLs to your existing web app.

Installation

Requirements

The package has been developed and tested to work with the following minimum requirements:

  • PHP 8.0
  • Laravel 8.0

Short URL requires either the BC Math or GMP PHP extensions in order to work.

Install the Package

You can install the package via Composer:

composer require ashallendesign/short-url

Publish the Config and Migrations

You can then publish the package's config file and database migrations by using the following command:

php artisan vendor:publish --provider="AshAllenDesign\ShortURL\Providers\ShortURLProvider"

Migrate the Database

This package contains two migrations that add two new tables to the database: short_urls and short_url_visits. To run these migrations, simply run the following command:

php artisan migrate

Usage

Building Shortened URLs

Quick Start

The quickest way to get started with creating a shortened URL is by using the snippet below. The ->make() method returns a ShortURL model that you can grab the shortened URL from.

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->make();
$shortURL = $shortURLObject->default_short_url;

Custom Keys

By default, the shortened URL that is generated will contain a random key. The key will be of the length that you define in the config files (defaults to 5 characters). Example: if a URL is https://webapp.com/short/abc123, the key is abc123.

You may wish to define a custom key yourself for that URL that is more meaningful than a randomly generated one. You can do this by using the ->urlKey() method. Example:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->urlKey('custom-key')->make();
$shortURL = $shortURLObject->default_short_url;

// Short URL: https://webapp.com/short/custom-key

Note: All of the URL keys are unique, so you cannot use a key that already exists in the database for another shortened URL.

Tracking Visitors

You may want to track some data about the visitors that have used the shortened URL. This can be useful for analytics. By default, tracking is enabled and all of the available tracking fields are also enabled. You can toggle the default options for the different parts of the tracking in the config file. Read further on in the Customisation section to see how to customise the default tracking behaviours.

Note: Even if the tracking options (such as track_ip_address) are enabled for a short URL, they won't be recorded unless the track_visits options is enabled. This can come in handy if you want to enable/disable tracking for a short URL without needing to individually set each option.

Enabling Tracking

If you want to override whether if tracking is enabled or not when creating a shortened URL, you can use the ->trackVisits() method. This method accepts a boolean but defaults to true if a parameter is not passed.

The example below shows how to enable tracking for the URL and override the config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->make();

The example below shows how to disable tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits(false)->make();
Tracking IP Address

If you want to override whether if IP address tracking is enabled or not when creating a shortened URL, you can use the ->trackIPAddress() method. This method accepts a boolean but defaults to true if a parameter is not passed.

The example below shows how to enable IP address tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackIPAddress()->make();
Tracking Browser & Browser Version

If you want to override whether if browser name and browser version tracking is enabled or not when creating a shortened URL, you can use the ->trackBrowser() and ->trackBrowserVersion() methods. This method accepts a boolean but defaults to true if a parameter is not passed.

The example below shows how to enable browser name tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackBrowser()->make();

The example below shows how to enable browser version tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackBrowserVersion()->make();
Tracking Operating System & Operating System Version

If you want to override whether if operating system name and operating system version tracking is enabled or not when creating a shortened URL, you can use the ->trackOperatingSystem() and ->trackOperatingSystemVersion() methods. These methods accept a boolean but default to true if a parameter is not passed.

The example below shows how to enable operating system name tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackOperatingSystem()->make();

The example below shows how to enable operating system version tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackOperatingSystemVersion()->make();
Tracking Device Type

If you want to override whether if device type tracking is enabled or not when creating a shortened URL, you can use the ->trackDeviceType() method. This method accepts a boolean but defaults to true if a parameter is not passed.

The example below shows how to enable device type tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackDeviceType()->make();
Tracking Referer URL

If you want to override whether if referer URL tracking is enabled or not when creating a shortened URL, you can use the ->trackRefererURL() method. This method accepts a boolean but defaults to true if a parameter is not passed.

The example below shows how to enable referer URL tracking for the URL and override the default config variable:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->trackVisits()->trackRefererURL()->make();

Custom Short URL Fields

There may be times when you want to add your own custom fields to the ShortURL model and store them in the database. For example, you might want to associate the short URL with a tenant, organisation, user, etc.

To do this you can use the beforeCreate method when building your short URL. This method accepts a closure that receives the AshAllenDesign\ShortURL\Models\ShortURL model instance before it's saved to your database.

The example below shows how to add a tenant_id field to the AshAllenDesign\ShortURL\Models\ShortURL model:

use AshAllenDesign\ShortURL\Models\ShortURL;
use AshAllenDesign\ShortURL\Facades\ShortURL as ShortUrlBuilder;

$tenantId = 123;

$shortURL = ShortUrlBuilder::destinationUrl($url)
    ->beforeCreate(function (ShortURL $model): void {
        $model->tenant_id = $tenantId;
    })
    ->make();

Please remember that to store custom fields in the database, you'll have to make sure those fields are added to the short_urls table. You can do this by creating a new migration that adds the fields to the table, or by updating the migrations that ship with this package.

Single Use

By default, all of the shortened URLs can be visited for as long as you leave them available. However, you may want to only allow access to a shortened URL once. Then any visitors who visit the URL after it has already been viewed will get a HTTP 404.

To create a single use shortened URL, you can use the ->singleUse() method.

The example below shows how to create a single use shortened URL:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')->singleUse()->make();

Enforce HTTPS

When building a shortened URL, you might want to enforce that the visitor is redirected to the HTTPS version of the destination URL. This can be particularly useful if you're allowing your web app users to create their own shortened URLS.

To enforce HTTPS, you can use the ->secure() method when building the shortened URL.

The example below shows how to create a secure shortened URL:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('http://destination.com')->secure()->make();

// Destination URL: https://destination.com

Forwarding Query Parameters

When building a short URL, you might want to forward the query parameters sent in the request to destination URL. By default, this functionality is disabled, but can be enabled by setting the forward_query_params config option to true.

Alternatively, you can also use the ->forwardQueryParams() method when building your shortened URL, as shown in the example below:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('http://destination.com?param1=test')->forwardQueryParams()->make();

Based on the example above, assuming that the original short URL's destination_url was https://destination.com, making a request to https://webapp.com/short/xxx?param1=abc&param2=def would redirect to https://destination.com?param1=test&param2=def

Redirect Status Code

By default, all short URLs are redirected with a 301 HTTP status code. But, this can be overridden when building the shortened URL using the ->redirectStatusCode() method.

The example below shows how to create a shortened URL with a redirect HTTP status code of 302:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('http://destination.com')->redirectStatusCode(302)->make();

Activation and Deactivation Times

By default, all short URLs that you create are active until you delete them. However, you may set activation and deactivation times for your URLs when you're creating them.

Doing this can be useful for marketing campaigns. For example, you may want to launch a new URL for a marketing campaign on a given date and then automatically deactivate that URL when the marketing campaign comes to an end.

The example below shows how to create a shortened URL that will be active from this time tomorrow onwards:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->activateAt(\Carbon\Carbon::now()->addDay())->make();

The example below shows how to create a shortened URL that will be active from this time tomorrow onwards and then is deactivated the day after:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->activateAt(\Carbon\Carbon::now()->addDay())
                          ->deactivateAt(\Carbon\Carbon::now()->addDays(2))
                          ->make();

Using a Custom Seed

By default, the package will use the ID of the last inserted short URL as the seed for generating a short URL's key. In some cases, you may want to use a custom seed instead. To do this, you can pass an integer to the generateKeyUsing method like so:

$builder = new \AshAllenDesign\ShortURL\Classes\Builder();

$shortURLObject = $builder->destinationUrl('https://destination.com')
   ->generateKeyUsing(12345)
   ->make();

Facade

If you prefer to use facades in Laravel, you can choose to use the provided ShortURL facade instead of instantiating the Builder class manually.

The example below shows an example of how you could use the facade to create a shortened URL:

<?php

namespace App\Http\Controllers;

use ShortURL;

class Controller
{
    public function index()
    {
        $shortURLObject = ShortURL::destinationUrl('https://destination.com')->make();
        ...
    }
}

Conditionals

The Builder class uses the Illuminate\Support\Traits\Conditionable trait, so you can use the when and unless methods when building your short URLs.

For example, let's take this block of code that uses if when building the short URL:

use AshAllenDesign\ShortURL\Classes\Builder;
 
$shortURLObject = (new Builder())
    ->destinationUrl('https://destination.com');

if ($request->date('activation')) {
    $builder = $builder->activateAt($request->date('activation'));
};

$shortURLObject = $builder->make();)

This could be rewritten using when like so:

use AshAllenDesign\ShortURL\Classes\Builder;
use Carbon\Carbon;

$shortURLObject = (new Builder())
   ->destinationUrl('https://destination.com')
   ->when(
       $request->date('activation'),
       function (Builder $builder, Carbon $activateDate): Builder  {
           return $builder->activateAt($activateDate);
       },
   )
   ->make();

Using the Shortened URLs

Default Route and Controller

By default, the shortened URLs that are created use the package's route and controller. The routes use the following structure: https://webapp.com/short/{urlKey}. This route uses the single-use controller that is found at \AshAllenDesign\ShortURL\Controllers\ShortURLController.

Custom Route

You may wish to use a different routing structure for your shortened URLs other than the default URLs that are created. For example, you might want to use https://webapp.com/s/{urlKey} or https://webapp.com/{urlKey}. You can customise this to suit the needs of your project.

To use the custom routing all you need to do is add a web route to your project that points to the ShortURLController and uses the {shortURLKey} field.

The example below shows how you could add a custom route to your web.php file to use the shortened URLs:

Route::get('/custom/{shortURLKey}', '\AshAllenDesign\ShortURL\Controllers\ShortURLController');

Note: If you use your own custom routing, you might want to disable the default route that the app provides. Details are provided for this in the Customisation section below.

Tracking

If tracking is enabled for a shortened URL, each time the link is visited, a new ShortURLVisit row in the database will be created. By default, the package is set to record the following fields of a visitor:

  • IP Address
  • Browser Name
  • Browser Version
  • Operating System Name
  • Operating System Version
  • Referer URL (the URL that the visitor originally came from)
  • Device Type (can be: desktop/mobile/tablet/robot)

Each of these fields can be toggled in the config files so that you only record the fields you need. Details on how to do this are provided for this in the Customisation section below.

Customisation

Customising the Default Route

Customising the Default URL

The package comes with a route that you can use for your short URLs. By default, this route uses your Laravel app's app.url config field to build the URL.

However, you might want to override this and use a different URL for your short URLs. For instance, you might want to use a different domain name for your short URLs.

To override the base URL, you can set the default_url config field. For example, to set the base URL to https://example.com, you can set the default_url in your config/short-url.php file like so:

'default_url' => 'https://example.com',

To use the your application's app.url config field, you can set the short_url.default_url field to null.

Customising the Prefix

The package comes with a route that you can use for your short URLs. By default, this route is /short/{shortURLKey}.

You might want to keep using this default route but change the /short/ prefix to something else. To do this, you can change the prefix field in the config.

For example, to change the default short URL to /s, you could change the config value like so:

'prefix' => 's',
Removing the Prefix

You may also remove the prefix from the default route completely. For example, if you want your short URL to be accessible via /{shortUrlKey}, then you can update the prefix config value to null like so:

'prefix' => null,
Defining Middleware

You may wish to run the default short URL through some middleware in your application. To do this, you can define the middleware that the route should use via the middleware config value.

For example, if you have a MyAwesomeMiddleware class, you could update your short-url config like so:

'middleware' => [
    MyAwesomeMiddleware::class,
],

You can also use this same approach to define middleware groups rather than individual middleware classes. For example, if you want your default short URL route to use the web middleware group, you could update your config like so:

'middleware' => [
    'web',
],

It's important to note that this middleware will only be automatically applied to the default short URL route that ships with the package. If you are defining your own route, you'll need to apply this middleware to your route yourself.

Disabling the Default Route

If you have added your own custom route to your project, you may want to block the default route that the package provides. You can do this by setting the following value in the config:

'disable_default_route' => true,

If the default route is disabled, any visitors who go to the /short/{shortURLKey} route will receive a HTTP 404.

You may want to manually prevent the route from being automatically registered and manually register it yourself in your own routes file. To do this you can add the following code to your routes file (e.g. web.php):

\AshAllenDesign\ShortURL\Facades\ShortURL::routes();

Default URL Key Length

When building a shortened URL, you have the option to define your own URL key or to randomly generate one. If one is randomly generated, the minimum length of it is determined from the config.

A minimum key length of 3 has been enforced for performance reasons.

For example, to create a shortened URL with a key length of 10 characters, you could set the following in the config:

'key_length' => 10,

By default, the shortened URLs that are created have a key length of 5.

Please be aware that the key length that you specify in the config is only a desirable length. It acts as a minimum length rather than a fixed length. For example, if the key_length is set to 3 in the config and there is a unique 3 character long key that hasn't been used yet, the key created will be 3 characters long. However, if all of the possible 3 character long keys are taken, a 4 character key will be created.

The Hashids library is used to assist with creating the URL keys.

Tracking Visits

By default, the package enables tracking of all the available fields on each URL built. However, this can be toggled in the config file.

Default Tracking

To disable tracking by default on all future short URLs that are generated, set the following in the config:

'tracking'   => [
        'default_enabled' => true,
        ...
]

Note: Disabling tracking by default won't disable tracking for any shortened URLs that already exist. It will only apply to all shortened URLs that are created after the config update.

Tracking Fields

You can toggle the default options for each of fields that can be tracked by changing them in the config. These options can then be overridden for each short URL at the point of creation, as shown in the Tracking Visitors section.

For example, the snippet below shows how we could record all of the fields apart from the IP address of the visitor:

'tracking'   => [
        ...
        'fields' => [
            'ip_address'               => false,
            'operating_system'         => true,
            'operating_system_version' => true,
            'browser'                  => true,
            'browser_version'          => true,
            'referer_url'              => true,
            'device_type'              => true,
        ],
    ],

Config Validation

By default, the values defined in the short-url.php config file are not validated. However, the library contains a validator that can be used to ensure that your values are safe to use. To enable the config validation, you can set the following option in the config:

'validate_config' => true,

Custom Database Connection

By default, Short URL will use your application's default database connection. But there may be times that you'd like to use a different connection. For example, you might be building a multi-tenant application that uses a separate connection for each tenant, and you may want to store the short URLs in a central database.

To do this, you can set the connection name using the connection config value in the config/short-url.php file like so:

'connection' => 'custom_database_connection_name',

Helper Methods

Visits

The ShortURL model includes a relationship (that you can use just like any other Laravel model relation) for getting the visits for a shortened URL.

To get the visits using the relationship, use ->visits or ->visits(). The example snippet belows shows how:

$shortURL = \AshAllenDesign\ShortURL\Models\ShortURL::find(1);
$visits = $shortURL->visits;

Find by URL Key

To find the ShortURL model that corresponds to a given shortened URL key, you can use the ->findByKey() method.

For example, to find the ShortURL model of a shortened URL that has the key abc123, you could use the following:

$shortURL = \AshAllenDesign\ShortURL\Models\ShortURL::findByKey('abc123');

Find by Destination URL

To find the ShortURL models that redirect to a given destination URL, you can use the ->findByDestinationURL() method.

For example, to find all of the ShortURL models of shortened URLs that redirect to https://destination.com, you could use the following:

$shortURLs = \AshAllenDesign\ShortURL\Models\ShortURL::findByDestinationURL('https://destination.com');

Tracking Enabled

To check if tracking is enabled for a short URL, you can use the ->trackingEnabled() method. It will return true if tracking is enabled, and false if not.

The following example shows how to check if a short URL has tracking enabled:

$shortURL = \AshAllenDesign\ShortURL\Models\ShortURL::first();
$shortURL->trackingEnabled();

Tracked Fields

To check which fields are enabled for tracking for a short URL, you can use the ->trackingFields() method. It will return an array with the names of each field that is currently enabled for tracking.

Note: Even if the tracking options (such as track_ip_address) are enabled for a short URL and returned, they won't be recorded unless the track_visits options is enabled. This can come in handy if you want to enable/disable tracking for a short URL without needing to individually set each option.

The following example shows how to get an array of all tracking-enabled fields for a short URL:

$shortURL = \AshAllenDesign\ShortURL\Models\ShortURL::first();
$shortURL->trackingFields();

Model Factories

The package comes with model factories included for testing purposes which come in handy when generating polymorphic relationships. The ShortURL model factory also comes with extra states that you may use when necessary, such as deactivated and inactive:

use AshAllenDesign\ShortURL\Models\ShortURL;

$shortUrl = ShortURL::factory()->create();

// URL is deactivated
$deactivatedShortUrl = ShortURL::factory()->deactivated()->create();

// URL is neither activated nor deactivated
$inactiveShortURL = ShortURL::factory()->inactive()->create();

If you are using your own custom model factory, you can define the factories that the ShortURL and ShortURLVisit models should use by updating the factories config field:

'factories' => [
    \AshAllenDesign\ShortURL\Models\ShortURL::class => \AshAllenDesign\ShortURL\Models\Factories\ShortURLFactory::class,
    \AshAllenDesign\ShortURL\Models\ShortURLVisit::class => \AshAllenDesign\ShortURL\Models\Factories\ShortURLVisitFactory::class
],

Events

Short URL Visited

Each time a short URL is visited, the following event is fired that can be listened on:

AshAllenDesign\ShortURL\Events\ShortURLVisited

If you are redirecting users with a 301 HTTP status code, it's possible that this event will NOT be fired if a visitor has already visited this short URL before. This is due to the fact that most browsers will cache the intended destination URL as a 'permanent redirect' and won't actually visit the short URL first.

For better results, use the 302 HTTP status code as most browsers will treat the short URL as a 'temporary redirect'. This means that the short URL will be visited in the browser and the event will be dispatched as expected before redirecting to the destination URL.

Testing

To run the package's unit tests, run the following command:

vendor/bin/phpunit

Security

If you find any security related issues, please contact me directly at [email protected] to report it.

Contribution

If you wish to make any changes or improvements to the package, feel free to make a pull request.

Note: A contribution guide will be added soon.

Credits

Changelog

Check the CHANGELOG to get more information about the latest changes.

Upgrading

Check the UPGRADE guide to get more information on how to update this library to newer versions.

License

The MIT License (MIT). Please see License File for more information.

Support Me

If you've found this package useful, please consider buying a copy of Battle Ready Laravel to support me and my work.

Every sale makes a huge difference to me and allows me to spend more time working on open-source projects and tutorials.

To say a huge thanks, you can use the code BATTLE20 to get a 20% discount on the book.

👉 Get Your Copy!

Battle Ready Laravel

short-url's People

Contributors

ash-jc-allen avatar bbredewold avatar boris-glumpler avatar carlosjs23 avatar daanrosendal avatar dependabot-preview[bot] avatar dependabot[bot] avatar hari avatar innoflash avatar iraziul avatar jangidgirish avatar joshtrebilco avatar julienarcin avatar logicsatinn avatar mywira avatar nathangiesbrecht avatar ockstadt avatar omerkoseoglu avatar peter279k avatar pointybeard avatar ryangjchandler avatar stevebauman avatar stylecibot avatar vendin avatar victor-emil avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

short-url's Issues

Geoip support?

First off, package looks awesome, thanks!

Would you accept a PR for country/city level geoip analytics?

Thanks!

We should not be specifying the salt as a constant.

Here the salt for hashids is given as a constant.

protected const HASH_SALT = 'AshAllenDesign\ShortURL';

The problem is that anyone who uses this library will be getting same key for a given $ID ( as in

$key = $this->hashids->encode($ID);
). So just like the key length can be specified from the .env the salt also can be taken from the .env a sensible default can be specified in the config as well.

singleUse and Multiple use

the single use function is only for single time use, but multiple usage like ifi need to use a like 3 time or 5 times what would i do for that ?

Make facade more Laravel-like

A have a simple change request. I wonder what you think about this, so I didn't create a PR yet.

The fact that it's a builder is typically hidden by Laravel.
So my suggestion is to change the facade to:

ShortUrl::to('http://destination.com')->make();

I guess you'd have to keep both facades alive for backwards compatibility until the next version, so this might not be important enough to you. But it sure looks a lot nicer :)

Optional config validation

Make the config validation optional. This should more than likely be turned off by default and then can be enabled with a config/environment variable.

This would give developers the confidence that their config is set up right but would avoid issues such as #34.

null prefix causes standard routes to 404

null prefix appears to cause 404 when using non default route.

Route::get('{shortURLKey}', '\AshAllenDesign\ShortURL\Controllers\ShortURLController'); route defined anywhere in routes file causes all routes to return 404

image

image

image

any route with any other prefix will work but not basic routes
image

see pr #123

Impossible to set custom route in shortURL make

Hi mate,
first of all may thanks for this great library, it's very cool!

I have an issue and I can't solve it. I'd like to have a custom route for verifying my generated short URLs, for example:

https://myapp.com/s/urlKey

In documentation I read that I need to disable default routing, so in my config/short-url.php I set:

disable_default_route = true

and in my routes/web.php I added this line:

Route::get('/s/{shortURLKey}', '\AshAllenDesign\ShortURL\Controllers\ShortURLController')->name('short');

The problem is that when I generate my short URLs I can't set the /s/ prefix in my default_short_url and in database they are stored with the default /short/ URL part. I think that the problem is in your Builder class, the method is insertShortURLIntoDatabase(). As you can see the array key 'default_short_url' doesn't check if I am using the default routing or not.

Can you please fix this? I don't want to override the Builder class or method because I don't want to lose compatibility with next versions, in any.

Thank you a lot,
bye.

Gives 404 if prefix is null

Hi
Gives me 404 for other urls if prefix is set to null and if i add some prefix value then it does not give any error...I have checked other issues some of them are facing same issue.Can you please help me out with this

Thanks

Ability to add custom domain?

Hi 👋

Is this possible to add a custom domain?

For e.g: our app_url set in config is https://example.com but we want the short url to be from https://eg.com. Right now the package generates the domain from config's app.url.

Can there be something like set a short domain (optional) and use that to construct the URL?

Set single_use to true has no effect when track_visits is false

Hi, I was reading through your code when I notice that the function handleVisit in Resolver does not make sense.

From what I understand, single_use urls will check the number of visits via the relationship with ShortURLVisit. But if track_visits is set to false, new relationship is never created and the check ($shortURL->single_use && count($shortURL->visits)) will always return false, therefore a supposedly "single_use" url can be used multiple times.

How to disable link?

I want to disable link, which mean i want to edit that link so it's cannot visited again. just like single use concept, but i want to disable the link only if i want to.

How can i achieve that?

A short URL with this key already exists.

I am facing the challenge of creating new short URLs where the key all ready exists - I am receiving the following error:

[2023-01-23 11:08:43] local.ERROR: A short URL with this key already exists. {"userId":1,"exception":"[object] (AshAllenDesign\ShortURL\Exceptions\ShortURLException(code: 0): A short URL with this key already exists. at /home/forge/dev1.rrdevours.monster/vendor/ashallendesign/short-url/src/Classes/Builder.php:546)
[stacktrace]

In my ideal situation if the key all ready exists I would like it to be overwritten with the new key (and all click and related data to be cleansed).

So far I have replaced the checkKeyDoesNotExist(): void function definition in the Builder class from

protected function checkKeyDoesNotExist(): void
    {
        if (ShortURL::where('url_key', $this->urlKey)->exists()) {
            throw new ShortURLException('A short URL with this key already exists.');
        }
    }
 protected function checkKeyDoesNotExist(): void
    {
        if (ShortURL::where('url_key', $this->urlKey)->exists()) {
            ShortURL::where('url_key', $this->urlKey)->delete();
        }
    }

However I would prefer not to modify the core - I was wondering if there is a config option that will allow me to achieve what I am after to ensure the behavior I am after is achieved.

Thank you!

single_use and deactivated_at

https://vimeo.com/748255348/fee77b1371

the above recorded for proof.
I have add two things
1- single use only
2-deactivaed_at
both are not working properly, you can see in the video single use only for 1 but i can open same link again and again.
and in the next the deactivated time is expire but link open and working

I need in this package the expired link remove automatically.

ShortURLVisit BelongsTo relationship foreign key incorrect

I noticed when working with Laravel Nova that the inverse relationship ShortURLVisit::shortURL() doesn't appear to be working correctly. This is the line:

return $this->belongsTo(ShortURL::class);

I think this is because the foreign key is calculated by converting ShortURL into it's snake-case equivalent and adding _id which becomes short_u_r_l_id (see, https://laravel.com/docs/9.x/eloquent-relationships#one-to-one-defining-the-inverse-of-the-relationship).

Adding the foreign key name fixed the problem:

    return $this->belongsTo(ShortURL::class, 'short_url_id');

Will create a PR for this shortly if you like.

Symfony RedirectResponse throws Exception for codes other than 301, 302, 303, 307, & 308

Currently, the Builder class only checks to see that the redirect code is an integer in the range of 300 to 399 (inclusive). See:

if ($statusCode < 300 || $statusCode > 399) {

This is a problem when it comes time to create an instance of Symfony\Component\HttpFoundation\RedirectResponse in ShortURLController (see lines src/Controllers/ShortURLController.php#L40:L43) since it will throw an exception for anything other than 301, 302, 303, 307, & 308 (See https://github.com/symfony/symfony/blob/6eef692c2aaefddb4c560867727f5424be16b128/src/Symfony/Component/HttpFoundation/RedirectResponse.php#L41)

Although the likelihood of a short URL being made that can trigger this exception is low, it is quite easy to do. E.g.:

(new Builder())->redirectStatusCode(399)->...->make();

One solution is to limit the possible redirect codes in Builder::redirectStatusCode(). E.g.

if (! in_array($statusCode, [301, 302, 303, 307, 308])) {
    throw new ShortURLException('The redirect status code must be a valid redirect HTTP status code.');
}

Forwarding short url parameters to destination when redirecting

Hello,

I would like to be able to forward and merge the url parameters when redirecting.

I mean, I want to create urls of this type:

https://website.com/short/abc?a=b => https://destination.com/long-url-with-parameters?utm_source=test&a=b

It is currently not working. The parameters (a=b) are deleted when redirecting.

I can implement this functionnality and make a PR.

But first, I would like to know your opinions on which option is the best:

  • Should it be the default behavior ? (forwading parameters)
  • Should it be customisable in the config.php file globally ? (forward parameters => true/false)
  • Should it be implemented on a per-url basis, in database ?

Thanks !

Configurable Models

Hello, i would like to use this package in my project, which has a tenant structure (tenancy for laravel).

The tenant structure i'm using changes the database connection from all the models to the tenant databases.
The problem i currently have is that i can't change the connection on the ShortURL and ShortURLVisit models to the central table.

I also want to add some additional fields to the ShortURL model, which would be possible if the model can be configurable.

Would you be willing to merge a PR which contains configurable models?
Where the path for both ShortURL and ShortURLVisit model are defined in the config.

Routing issues with non prefixed routes

Hiya

Have tested this a little further from the issue i posted yesterday.

Reproduce the issue as follows.

  1. composer create-project laravel/laravel short
  2. cd short
  3. composer require ashallendesign/short-url
  4. php artisan vendor:publish --provider="AshAllenDesign\ShortURL\Providers\ShortURLProvider"
  5. set 'prefix' => null,
  6. php artisan migrate
  7. configure routes
Route::get('/', function () { // works
    return 'root';
});

Route::get('/basic', function () { // 404
    return 'basic route';
});

Route::middleware([])->group(function () { // works
    Route::get('/grouped', function () {
        return 'grouped route';
    });
});

Route::group(['prefix' => 'nested'], function () { // works
    Route::get('/route', function () {
        return 'a test route';
    });
});
  1. observe the route /basic gives 404

composer.json

"require": {
        "php": "^8.0.2",
        "ashallendesign/short-url": "^7.1",
        "guzzlehttp/guzzle": "^7.2",
        "laravel/framework": "^9.19",
        "laravel/sanctum": "^3.0",
        "laravel/tinker": "^2.7"
    },

image

Is there possibility to add user_id to the short_urls table and populate that if user is authorized?

Thank you for creating such a nice package. I really like it.
Appreciate your hard work!

I want to associate every short URL to user when the user is authorized. Is there possibility to add user_id to the short_urls table and populate that if user is authorized?
Adding a column is easy, I can create new migration and run that. The problem is populating that new field.
I searched in existing closed issued, but could not found such feature.
I also had a look at the code and found that there is no possibility to "inject" any kind of new data into short_urls table.

Am I missing something, or this feature is not available?

[Update]
The only solution I see at the moment is to get the object from ->make() method and update it.

Cheers

For custom urls

Hello,

You added this code Builder.php line 428:

        'default_short_url'              => config('app.url').'/short/'.$this->urlKey,

which I think it will be better if we can have it as:

        'default_short_url'              => config('app.url').'/'.config('short-url.short').$this->urlKey,

and in the short-url.php we can add it there for customization:

'short' => 'short/',

This way if we need to make the site like https://domain.com/s/{shortURLKey}

all that we need to do is changing the config file to:

'short' => 's/',

and if we need to make it direct like https://domain.com/{shortURLKey} then will change the config file to:

'short' => '',

I know that by 'disable_default_route' to true will do the job but I notice that when we create it directly to the db will be faster.

Any thughts?

Short URL redirect not working

After installing the package through composer I am able to create shortlinks and also define custom keys and return them from the route - however after copy pasting them into a browser I am unable to redirect. So far I have tried creating a custom route in my routes file as so:

Route::get('/offer/{shortURLKey}', '\AshAllenDesign\ShortURL\Controllers\ShortURLController');

and have also tried adding the routes directly into my route file as so

\AshAllenDesign\ShortURL\Facades\ShortURL::routes();

In the end I am stuck in Chrome with the loading screen and no redirect - any input as to why this could be happening would be much appreciated.

Thank you!

Embracing Testing

Hey @ash-jc-allen, Is it possible to have factories support for the two models, ShortURL and ShortURLVisits for easier testing??

I mean, somebody could extend the models and add the HasFactory trait as I did, but it got confusing trying to generate data using the relationship between the two models.

What do you think @ash-jc-allen ??

Issue with config('short-url.default_url')

I can make PR if needed but I think the logic on the default_url is a bit flawed.

Real world example for short urls:
bitly.com => bit.ly
google.com => goo.gl

I wanted to implement something like so:
livingdonorproject.org => ldpl.ink

I configured Laravel to work with both domains and installed the Short URL package. What I am noticing is that if I set the prefix to null then what happens is the default routing for Laravel app is broken and being overloaded by the Short URL package.

For instance my /login path is returning 404.

REGARDLESS of me setting the default_url on the Builder on line 209 in the routes() method you should be setting the Routes::domain($url) by default. It should be utilizing either the config('short-url.default_url') or config('app.url').

This will allow the package to overload the default app.url routes and work as intended with prefixes or no prefix while also allowing a secondary domain that applies all the routes ONLY to that domain when using the config('short-url.default_url')

What are your thoughts on this?

Editing existing links don´t work

Hi! How are you? Fine I hope!

I made a little sytem to generate short urls using your lib, it's amazing.

But I'm facing a problem, I can't edit existing links. It shows the edited value, I can see it on database, but when I try to access the shortened url, it goes to the old destination. There's something else that I need to do?

This is my update function:

    public function update(Request $request, ShortURL $link)
    {      
        //dd($request);
        $link = ShortURL::find($request->id);
        $link->destination_url = $request->old_link;
        $link->url_key = $request->alias;
        $link->default_short_url = $request->new_link;
        $link->update();

        // redirect
        //Session::flash('message', 'Link atualizado!');
        return back();
    }

Thanks!

Manually rerun migrations

I've recently had an issue where my database was wiped clean, I am attempting to remake the structure as my database backup I have just discovered only contained values and not table structure inserts.

I am attempting to redo the migrations of the short-url from the vendor folder with this command:

php artisan migrate --path=vendor/ashallendesign/short-url/database/migrations

however I am getting back the notice:

Nothing to migrate

If you have any input on how I can manually rerun the database migrations of your plugin it would be much apprecaited.

Thank you!

The table is missing indexes

short_urls:

  • url_key: here, here, here, here
  • destination_url: here
  • created_by: here - it's better to write ->latest('id') so that you don't have to create an index on the created_at field.

short_url_visits:

  • short_url_id: here
Schema::table('short_urls', function (Blueprint $table) {
    $table->unique('url_key');
    $table->index('destination_url');
    $table->index('created_by');
});

Schema::table('short_url_visits', function (Blueprint $table) {
    $table->index('short_url_id');
});

default behaviour is changing http destination urls to https

When I set a destinationUrl that is http (not https) its changed to https after building the ShortUrl.

$route = route('invoices.print', $factura->id); // http://localhost:8000/invoices/3/print
$shortURLObject = $builder->destinationUrl($route)->make();
$shortURLObject->destionation_url; // https://localhost:8000/invoices/3/print

We already have a method to force HTTPS:

Enforce HTTPS
When building a shortened URL, you might want to enforce that the visitor is redirected to the HTTPS version of the destination URL. This can be particularly useful if you're allowing your web app users to create their own shortened URLS.
To enforce HTTPS, you can use the ->secure() method when building the shortened URL.

So I think the default behaviour is ambiguous with this method, should we change it to respect http urls?

Edit: I've found that using ->secure(false) solves this, but I still think that replacing http by https by default can be unexpected.

Click even t(ShortURLVisited) only registering once on live

Any idea why my ShortURLVisited event is only triggered once (the first time) the URL is clicked but not others? Is there anything I should check? Thank you

my click event:
https://gist.github.com/HeadStudios/b2ffd252e82be5f80a2b0085bedf7057

event service provider
https://gist.github.com/HeadStudios/73e78099a8bff75733d83f6c37d3a899

Thank you!
Video for context: https://p147.p4.n0.cdn.getcloudapp.com/items/o0uYr8xd/5e74cd1b-4289-4110-87d8-539872cdbbe2.mp4

Note: it does seem to work if I open in Incognito tab and go to the short url - just strange behavior that's not happening on local and I'm trying to discover and investigate why. Any input much appreciated thank you for your review.

Change behaviour to return existing url by default if exists in database.

Currently, builder will fail if the short url already exists, this behaviour is not idea because it forces you to have extra boiler plate code to use the package when a simple check can resolve this within the package. You can set a flag to throw the exception or add it is a configuration value.

I am referring to these:

Builder.php L:449
        $this->setOptions();

        $this->checkKeyDoesNotExist();

        $shortURL = $this->insertShortURLIntoDatabase();

        $this->resetOptions();

Prefix in config is not being used to generate link

The source code here is different from what composer requires which comes with a hardcoded prefix(short) and would not apply the prefix set in the config file.
This makes it impossible to change the prefix via config

protected function insertShortURLIntoDatabase(): ShortURL
{
return ShortURL::create([
'destination_url' => $this->destinationUrl,
'default_short_url' => config('app.url').'/short/'.$this->urlKey,
'url_key' => $this->urlKey,
'single_use' => $this->singleUse,
'track_visits' => $this->trackVisits,
'redirect_status_code' => $this->redirectStatusCode,
'track_ip_address' => $this->trackIPAddress,
'track_operating_system' => $this->trackOperatingSystem,
'track_operating_system_version' => $this->trackOperatingSystemVersion,
'track_browser' => $this->trackBrowser,
'track_browser_version' => $this->trackBrowserVersion,
'track_referer_url' => $this->trackRefererURL,
'track_device_type' => $this->trackDeviceType,
'activated_at' => $this->activateAt,
'deactivated_at' => $this->deactivateAt,
]);
}

Default Tracking set False

'tracking' => [
'default_enabled' => false,
]
why it save null value into this short_url_visits table into database, as i set it false.

Suggestion: Make the base URL configurable and prefix optional

Hello,

Thanks for your solution, it's very nice.

I'd like to make a suggestion, to let the application users to choose the base URL and make the prefix optional.

At the moment, I can see that the default_sort_url is set to config('app.url').'/'.$this->prefix().'/'.$this->urlKey, which makes impossible to have a domain different than the application one for the shortened URL.

Use case:

Let's say I have a full domain app that's https://full-domain-app.com, but when shortening the URL, I want to use https://fda.is, so the URL is even shorter. Let's say I also want to remove the prefix altogether.

Something like this could do the trick:

...
    protected function insertShortURLIntoDatabase(): ShortURL
    {
        $baseUrl = $this->baseUrl ?? config('app.url');

        $prefix = $this->prefix() ? "{$this->prefix()}/" : '';

        return ShortURL::create([
            'destination_url'                => $this->destinationUrl,
            'default_short_url'              => "{$baseUrl}/{$prefix}".$this->urlKey,
...

I could create a PR for that, but I think it's better to understand if that's something you would like to add to your package.

Thanks!

Whitelisting IPs

Any possibility for whitelisting IP addresses? I want to check that link works without counting visit against it before sending

Is it possible to add a column to the `short_urls` table?

Hello, thanks for this cool package.

I've been using this package for some time, and now I want to add a new feature to my app, namely titles for URLs, just like in Bitly.

When I went back through the documentation, I didn't find an option for that. Or am I not careful enough?

I'm trying to fork to create the feature, but I'm having a problem where the insertShortURLIntoDatabase() method in the Builder class uses mass assignment to add data, which means, need to add those extra columns to the $fillable property in the model. But until now I couldn't do it.

For now, on the controller I do manual changes using Eloquent. But of course this is very ineffective considering it will cause 2 queries in one action.

Please respond and help, thank you.

change the path - from config('app.url') to config('short-url.url')

Hi
short-url.
I suggest that the default path that use te app path will use by default the app path, but in case user want to change it ( like i need to do in my case ) it will use what was set in short-url config

in config file add
'url' => config('app.url') // this is the default
in case user want to change it will be e.g.
'url'= >'https;//bla.me'

need to change all code to support this new config , but it is very easy to be done
this change it important for systems that have basic long url

error during installation

Clean install using Laravel 6.1.0, PHP 7.3.14, Fedora 31.

The following error is returned each time I put composer install, or any php artisan command.

ramius/package-versions: Generating version class...
ocramius/package-versions: ...done generating version class
> post-autoload-dump: Illuminate\Foundation\ComposerScripts::postAutoloadDump
> post-autoload-dump: @php artisan package:discover --ansi

   AshAllenDesign\ShortURL\Exceptions\ValidationException  : The config URL length is not a valid integer.

  at /home/carlos/Dev/ispmanager/vendor/ashallendesign/short-url/src/Classes/Validation.php:36
    32|     {
    33|         $urlLength = config('short-url.key_length');
    34| 
    35|         if (! is_int($urlLength)) {
  > 36|             throw new ValidationException('The config URL length is not a valid integer.');
    37|         }
    38| 
    39|         if ($urlLength < 3) {
    40|             throw new ValidationException('The config URL length must be 3 or above.');

  Exception trace:

  1   AshAllenDesign\ShortURL\Classes\Validation::validateKeyLength()
      /home/carlos/Dev/ispmanager/vendor/ashallendesign/short-url/src/Classes/Validation.php:18

  2   AshAllenDesign\ShortURL\Classes\Validation::validateConfig()
      /home/carlos/Dev/ispmanager/vendor/ashallendesign/short-url/src/Providers/ShortURLProvider.php:48

  Please use the argument -v to see more details.

Thanks you.

Add support for bulk data

Would it be possible to add a bulk upload method?

I noticed that in the individual creation a query is generated to obtain the last ID, +1 is added, the hash is generated and it is checked again if it already exists. If it exists, the process is repeated.

https://github.com/ash-jc-allen/short-url/blob/master/src/Classes/KeyGenerator.php#L40

public function generateRandom(): string
    {
        $ID = $this->getLastInsertedID();

        do {
            $ID++;
            $key = $this->hashids->encode($ID);
        } while (ShortURL::where('url_key', $key)->exists());

        return $key;
    }

But this process creates problems if we want to run multiple jobs in parallel.

Probably a case that I still do not have very clear would be to insert the destinations in a massive way and with another method generate the hashes.

// destination , url_key
$data= [
     ['https://five.xyz','KeyFive'],
       ...
     ['https://monday.xyz', null],
     ['https://orange.xyz','oRanGe'],
];

$destinations = (new Builder())->create($data); // only insert data

$destinations->each( function($destination) {
   // generate hash process
});

Using a Custom Route not working

Hello,

I followed the instructions to enable custom route, but still getting the previous default url.

I added the below to web.php

Route::get('/test11/{shortURLKey}', '\AshAllenDesign\ShortURL\Controllers\ShortURLController');

Then updated short-url.php config as below

    'disable_default_route' => false,
    'default_url' => 'https://example.com',

But still getting [/short/Vp9gN](https://mydomain.com/short/Vp9gN)"
when using

return [ShortURL::destinationUrl('https://google.com')->make()->default_short_url]

Any help?

DataBase Connectivity

i want to set database name for migration file but i don't see any option for set database name.so i tried like this

i changed file config/short-url.php
like this :

/*
|--------------------------------------------------------------------------
| Connection
|--------------------------------------------------------------------------
|
| Here you may configure the connection information for each server that
| is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more.
|
|
*/
'connection' => env('SHORT_URL_CONNECTION', null),

/*

and also changes - vendor/AshAllenDesign\ShortURL\Models\ShortURL.php AND vendor/AshAllenDesign\ShortURL\Models\ShortURLVisit.php

i added this line in both file

connection = config('short-url.connection'); } } ?>

[FR] make it possible to overwrite the models

It would be amazing, if we could overwrite the models. For example iI would need to add more columns to the ShortURLVisit.
Maybe add it to the config as described here: https://freek.dev/2442-strategies-for-making-laravel-packages-customizable

Let me explain my example a bit more: Would need to add a custom column called type which would just be a string/enum column with lets say qr and link values. My target url would include a query param ?type=qr and this should be tracked in the short_url_visits.type column.

By using my own model i can add the column to the fillable properties. The next bit that would need customising is the trackVisit function in the Resolver.

Link Clicked Events not being "caught" by Listener

I am having difficulties listening to the ShortURLVisited event from my links.

Here are the steps I have taken so far to create a proof of concept of dumping the arguments passed to the ShortURLVisited event function ($shortURL and $visit).

  1. I have created an Event listener for the ShortURLVisited event as per below Gist in my app/Listeners/PhoneMessageLinkClicked.php
    https://gist.github.com/HeadStudios/01dd319ef0715ae9b53b7a209268e2ea
  2. I have added the listener to the app/Providers/EventServiceProvider.php as per below Gist:
    https://gist.github.com/HeadStudios/6165ceb6d06e5513e44d98e72f19b8a9
  3. I have created a shortlink in a route and ensured that it had the trackVisits() function
Route::get('/airbooker', function() {

    $builder = new \AshAllenDesign\ShortURL\Classes\Builder();
    $shortURLObject = $builder->destinationUrl('https://google.com')->urlKey('heyhobois')->trackVisits()->make();
    $shortURL = $shortURLObject->default_short_url;
    return $shortURL;
  1. I can confirm the short URL was returned and that clicking on it twice added two rows to the short_url_visits table which was correctly linked to the shortlink I had created
  2. However when those links were clicked nothing was output in my Laravel Dump as expected as per the Listener I had created.

Any input or suggestions on how I can fix my challenge of Events not being "heard" would be much appreciated.

Thank you so much and congrats on the awesome plugin!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.