Skip to content

Commit

Permalink
UTM_ Tracking on Clicks (#12)
Browse files Browse the repository at this point in the history
- Added ShortUrlTracing Model/Migration/Factory. 
- Added withTracing() option to UrlService builder
- Added ability to filter clicks based on UTM parameters. 
- Added eager loading relationships when using FindBy methods on UrlService.
- Updated Test Coverage.
- Updated Readme with UTM Support Information.
  • Loading branch information
yordadev authored Sep 7, 2022
1 parent b9805e0 commit 54d2d1c
Show file tree
Hide file tree
Showing 28 changed files with 1,161 additions and 14 deletions.
89 changes: 85 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,32 +62,56 @@ $url = UrlService::shorten('something-extremely-long.com/even/longer?ref=with&so
->withOpenLimit(2)
->withOwnership(Model::find(1))
->withPassword('password')
->withTracing([
'utm_id' => 't123',
'utm_campaign' => 'campaign_name',
'utm_source' => 'linkedin',
'utm_medium' => 'social',
])
->build();
// http(s)://host/prefix/identifier;
```

Finding Existing Short Urls

```php

/**
* Find a Short URL by its identifier
*/
$shortUrl = UrlService::findByIdentifier('identifier');
// returns instance of ShortUrl Model.


/**
* Find a Short URL by its hashed signature
*/
$shortUrl = UrlService::findByHash(md5('long_url'));
// returns instance of ShortUrl Model.


/**
* Find a Short URL by its plain text long url string
*/
$shortUrl = UrlService::findByPlainText('long_url');
// returns instance of ShortUrl Model.

/**
* Will return an instance of Models/ShortUrl or throw UrlRepository('Unable to locate Short URL')
* Find shortUrls by UTM combinations.
*
* Note* This method only accepts the following array fields:
* - utm_id
* - utm_campaign
* - utm_source
* - utm_medium
* - utm_content
* - utm_term
*/
$shortUrlCollection = UrlService::findByUtmCombination([
'utm_campaign' => 'alpha',
'utm_source' => 'bravo',
'utm_medium' => 'testing'
])
// returns an instance of Eloquent Collection of ShortUrl Models.
```

Getting Click Information
Expand All @@ -107,6 +131,15 @@ dd($clicks);
'hashed' => ...,
'plain_text' => ...,
'limit' => ...,
'tracing' => [
'id' => ...,
'utm_id' => ...,
'utm_source' => ...,
'utm_medium' => ...,
'utm_campaign' => ...,
'utm_content' => ...,
'utm_term' => ...,
]
'created_at' => ...,
'updated_at' => ...
],
Expand All @@ -133,7 +166,7 @@ dd($clicks);
'id' => ...,
'name' => ...,
'alias' => ...,
]
],
]
],
'total' => 1
Expand Down Expand Up @@ -188,6 +221,35 @@ $clicks = ClickService::get([
]);
```

Filtered Clicks by UTM parameter(s). These Can be filtered together or individually.
```php
$clicks = ClickService::get([
'utm_id' => [
'xyz',
'yxz'
],
'utm_source' => [
'linkedin',
'facebook'
],
'utm_medium' => [
'social'
],
'utm_campaign' => [
'sponsored',
'affiliate'
],
'utm_content' => [
'xyz',
'yxz'
],
'utm_term' => [
'marketing+software',
'short+url'
],
]);
```

Iterate Through Results With Batches

```php
Expand All @@ -214,12 +276,31 @@ $clicks = ClickService::get([
],
'status' => [
'active'
],
],
'utm_campaign' => [
'awareness'
],
'utm_source' => [
'github'
],
'limit' => 500
'offset' => 1500
]);
```

## UTM Support

When creating a Short URL, the following UTM parameters are available to attach to the Short URL for advanced tracking of your Short Urls.

- utm_id
- utm_campaign
- utm_source
- utm_medium
- utm_content
- utm_term

UTM information is hidden in the Short URL identifier and clicks are filterable by UTM parameters.

## Testing

```bash
Expand Down
78 changes: 78 additions & 0 deletions src/Builders/ClickQueryBuilder/ClickQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,84 @@

class ClickQueryBuilder extends Builder
{
/**
* @param array $utm_terms
* @return ClickQueryBuilder
*/
public function whereInTracingTerm(array $utm_terms): ClickQueryBuilder
{
return $this->whereIn('short_url_id', function ($query) use ($utm_terms) {
$query->from('short_url_tracings');
$query->whereIn('utm_term', $utm_terms);
$query->select('short_url_id');
});
}

/**
* @param array $utm_sources
* @return ClickQueryBuilder
*/
public function whereInTracingSource(array $utm_sources): ClickQueryBuilder
{
return $this->whereIn('short_url_id', function ($query) use ($utm_sources) {
$query->from('short_url_tracings');
$query->whereIn('utm_source', $utm_sources);
$query->select('short_url_id');
});
}

/**
* @param array $utm_mediums
* @return ClickQueryBuilder
*/
public function whereInTracingMedium(array $utm_mediums): ClickQueryBuilder
{
return $this->whereIn('short_url_id', function ($query) use ($utm_mediums) {
$query->from('short_url_tracings');
$query->whereIn('utm_medium', $utm_mediums);
$query->select('short_url_id');
});
}

/**
* @param array $utm_ids
* @return ClickQueryBuilder
*/
public function whereInTracingId(array $utm_ids): ClickQueryBuilder
{
return $this->whereIn('short_url_id', function ($query) use ($utm_ids) {
$query->from('short_url_tracings');
$query->whereIn('utm_id', $utm_ids);
$query->select('short_url_id');
});
}

/**
* @param array $utm_contents
* @return ClickQueryBuilder
*/
public function whereInTracingContent(array $utm_contents): ClickQueryBuilder
{
return $this->whereIn('short_url_id', function ($query) use ($utm_contents) {
$query->from('short_url_tracings');
$query->whereIn('utm_content', $utm_contents);
$query->select('short_url_id');
});
}

/**
* @param array $utm_campaigns
* @return ClickQueryBuilder
*/
public function whereInTracingCampaign(array $utm_campaigns): ClickQueryBuilder
{
return $this->whereIn('short_url_id', function ($query) use ($utm_campaigns) {
$query->from('short_url_tracings');
$query->whereIn('utm_campaign', $utm_campaigns);
$query->select('short_url_id');
});
}

/**
* @param array $outcomes
* @return ClickQueryBuilder
Expand Down
36 changes: 36 additions & 0 deletions src/Builders/UrlBuilder/Options/WithTracing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace YorCreative\UrlShortener\Builders\UrlBuilder\Options;

use Illuminate\Support\Collection;
use YorCreative\UrlShortener\Builders\UrlBuilder\UrlBuilderOptionInterface;
use YorCreative\UrlShortener\Exceptions\TracingRepositoryException;
use YorCreative\UrlShortener\Exceptions\UrlRepositoryException;
use YorCreative\UrlShortener\Repositories\TracingRepository;
use YorCreative\UrlShortener\Repositories\UrlRepository;

class WithTracing implements UrlBuilderOptionInterface
{
/**
* @param Collection $shortUrlCollection
*
* @throws UrlRepositoryException
* @throws TracingRepositoryException
*/
public function resolve(Collection &$shortUrlCollection): void
{
$sanitizedUtmParameters = TracingRepository::sanitizeUtmArray(
$shortUrlCollection->get('utm_parameters')
);

$trace = [
'short_url_id' => UrlRepository::findByIdentifier(
$shortUrlCollection->get('identifier')
)->id,
];

$trace = array_merge($trace, $sanitizedUtmParameters);

TracingRepository::create($trace);
}
}
16 changes: 16 additions & 0 deletions src/Builders/UrlBuilder/UrlBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use YorCreative\UrlShortener\Builders\UrlBuilder\Options\WithOpenLimit;
use YorCreative\UrlShortener\Builders\UrlBuilder\Options\WithOwnership;
use YorCreative\UrlShortener\Builders\UrlBuilder\Options\WithPassword;
use YorCreative\UrlShortener\Builders\UrlBuilder\Options\WithTracing;
use YorCreative\UrlShortener\Exceptions\UrlBuilderException;
use YorCreative\UrlShortener\Exceptions\UrlServiceException;
use YorCreative\UrlShortener\Services\UtilityService;
Expand Down Expand Up @@ -161,6 +162,21 @@ public function withOwnership(Model $model): UrlBuilder
return $this;
}

/**
* @param array $utm_parameters
* @return $this
*/
public function withTracing(array $utm_parameters)
{
$this->shortUrlCollection->put('utm_parameters', $utm_parameters);

$this->options->add(
new WithTracing()
);

return $this;
}

/**
* @throws Exception
*/
Expand Down
14 changes: 14 additions & 0 deletions src/Exceptions/TracingRepositoryException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace YorCreative\UrlShortener\Exceptions;

use Exception;
use Throwable;

class TracingRepositoryException extends Exception
{
public function __construct($message = '', $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
8 changes: 8 additions & 0 deletions src/Models/ShortUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ class ShortUrl extends Model
'laravel_through_key',
];

/**
* @return HasOne
*/
public function tracing(): HasOne
{
return $this->hasOne(ShortUrlTracing::class, 'short_url_id', 'id');
}

/**
* @return bool
*/
Expand Down
54 changes: 54 additions & 0 deletions src/Models/ShortUrlTracing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace YorCreative\UrlShortener\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use YorCreative\UrlShortener\Traits\PublishableHasFactory;

class ShortUrlTracing extends Model
{
use PublishableHasFactory;

/**
* @var bool
*/
public $incrementing = true;

/**
* @var string
*/
protected $table = 'short_url_tracings';

/**
* @var string
*/
protected $primaryKey = 'id';

/**
* @var string[]
*/
protected $fillable = [
'short_url_id',
'utm_id',
'utm_source',
'utm_medium',
'utm_campaign',
'utm_content',
'utm_term',
];

protected $hidden = [
'deleted_at',
'created_at',
'updated_at',
];

/**
* @return BelongsTo
*/
public function shortUrl(): BelongsTo
{
return $this->belongsTo(ShortUrl::class, 'short_url_id', 'id');
}
}
Loading

0 comments on commit 54d2d1c

Please sign in to comment.