mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-23 07:33:58 -06:00
Added support for an optional title field in short URLs
This commit is contained in:
parent
31a7212a71
commit
430c407106
32
data/migrations/Version20210202181026.php
Normal file
32
data/migrations/Version20210202181026.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20210202181026 extends AbstractMigration
|
||||
{
|
||||
private const TITLE = 'title';
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
$this->skipIf($shortUrls->hasColumn(self::TITLE));
|
||||
|
||||
$shortUrls->addColumn(self::TITLE, Types::STRING, [
|
||||
'notnull' => false,
|
||||
'length' => 512,
|
||||
]);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
$this->skipIf(! $shortUrls->hasColumn(self::TITLE));
|
||||
$shortUrls->dropColumn(self::TITLE);
|
||||
}
|
||||
}
|
@ -84,4 +84,10 @@ return static function (ClassMetadata $metadata, array $emConfig): void {
|
||||
->build();
|
||||
|
||||
$builder->addUniqueConstraint(['short_code', 'domain_id'], 'unique_short_code_plus_domain');
|
||||
|
||||
$builder->createField('title', Types::STRING)
|
||||
->columnName('title')
|
||||
->length(512)
|
||||
->nullable()
|
||||
->build();
|
||||
};
|
||||
|
@ -38,6 +38,7 @@ class ShortUrl extends AbstractEntity
|
||||
private ?string $importSource = null;
|
||||
private ?string $importOriginalShortCode = null;
|
||||
private ?ApiKey $authorApiKey = null;
|
||||
private ?string $title = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
@ -72,6 +73,7 @@ class ShortUrl extends AbstractEntity
|
||||
$instance->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode($instance->shortCodeLength);
|
||||
$instance->domain = $relationResolver->resolveDomain($meta->getDomain());
|
||||
$instance->authorApiKey = $meta->getApiKey();
|
||||
$instance->title = $meta->getTitle();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
@ -157,6 +159,11 @@ class ShortUrl extends AbstractEntity
|
||||
return $this->maxVisits;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function update(
|
||||
ShortUrlEdit $shortUrlEdit,
|
||||
?ShortUrlRelationResolverInterface $relationResolver = null
|
||||
|
@ -28,6 +28,7 @@ final class ShortUrlMeta
|
||||
private ?bool $validateUrl = null;
|
||||
private ?ApiKey $apiKey = null;
|
||||
private array $tags = [];
|
||||
private ?string $title = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
@ -76,6 +77,7 @@ final class ShortUrlMeta
|
||||
) ?? DEFAULT_SHORT_CODES_LENGTH;
|
||||
$this->apiKey = $inputFilter->getValue(ShortUrlInputFilter::API_KEY);
|
||||
$this->tags = $inputFilter->getValue(ShortUrlInputFilter::TAGS);
|
||||
$this->title = $inputFilter->getValue(ShortUrlInputFilter::TITLE);
|
||||
}
|
||||
|
||||
public function getLongUrl(): string
|
||||
@ -160,4 +162,9 @@ final class ShortUrlMeta
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\Core\Model;
|
||||
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
|
||||
use function array_pad;
|
||||
use function explode;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
@ -50,9 +51,9 @@ final class ShortUrlsOrdering
|
||||
|
||||
/** @var string|array $orderBy */
|
||||
if (! $isArray) {
|
||||
$parts = explode('-', $orderBy);
|
||||
$this->orderField = $parts[0];
|
||||
$this->orderDirection = $parts[1] ?? self::DEFAULT_ORDER_DIRECTION;
|
||||
[$field, $dir] = array_pad(explode('-', $orderBy), 2, null);
|
||||
$this->orderField = $field;
|
||||
$this->orderDirection = $dir ?? self::DEFAULT_ORDER_DIRECTION;
|
||||
} else {
|
||||
$this->orderField = key($orderBy);
|
||||
$this->orderDirection = $orderBy[$this->orderField];
|
||||
|
@ -34,6 +34,7 @@ class ShortUrlDataTransformer implements DataTransformerInterface
|
||||
'tags' => invoke($shortUrl->getTags(), '__toString'),
|
||||
'meta' => $this->buildMeta($shortUrl),
|
||||
'domain' => $shortUrl->getDomain(),
|
||||
'title' => $shortUrl->getTitle(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ class ShortUrlInputFilter extends InputFilter
|
||||
public const VALIDATE_URL = 'validateUrl';
|
||||
public const API_KEY = 'apiKey';
|
||||
public const TAGS = 'tags';
|
||||
public const TITLE = 'title';
|
||||
|
||||
private function __construct(array $data, bool $requireLongUrl)
|
||||
{
|
||||
@ -87,6 +88,8 @@ class ShortUrlInputFilter extends InputFilter
|
||||
|
||||
$this->add($this->createBooleanInput(self::FIND_IF_EXISTS, false));
|
||||
|
||||
// This cannot be defined as a boolean input because it can actually have 3 values, true, false and null.
|
||||
// Defining it as boolean will make null fall back to false, which is not the desired behavior.
|
||||
$this->add($this->createInput(self::VALIDATE_URL, false));
|
||||
|
||||
$domain = $this->createInput(self::DOMAIN, false);
|
||||
@ -100,5 +103,7 @@ class ShortUrlInputFilter extends InputFilter
|
||||
$this->add($apiKeyInput);
|
||||
|
||||
$this->add($this->createTagsInput(self::TAGS, false));
|
||||
|
||||
$this->add($this->createInput(self::TITLE, false));
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,13 @@ class MercureUpdatesGeneratorTest extends TestCase
|
||||
* @test
|
||||
* @dataProvider provideMethod
|
||||
*/
|
||||
public function visitIsProperlySerializedIntoUpdate(string $method, string $expectedTopic): void
|
||||
public function visitIsProperlySerializedIntoUpdate(string $method, string $expectedTopic, ?string $title): void
|
||||
{
|
||||
$shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['customSlug' => 'foo', 'longUrl' => '']));
|
||||
$shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData([
|
||||
'customSlug' => 'foo',
|
||||
'longUrl' => '',
|
||||
'title' => $title,
|
||||
]));
|
||||
$visit = new Visit($shortUrl, Visitor::emptyInstance());
|
||||
|
||||
$update = $this->generator->{$method}($visit);
|
||||
@ -50,6 +54,7 @@ class MercureUpdatesGeneratorTest extends TestCase
|
||||
'maxVisits' => null,
|
||||
],
|
||||
'domain' => null,
|
||||
'title' => $title,
|
||||
],
|
||||
'visit' => [
|
||||
'referer' => '',
|
||||
@ -62,7 +67,7 @@ class MercureUpdatesGeneratorTest extends TestCase
|
||||
|
||||
public function provideMethod(): iterable
|
||||
{
|
||||
yield 'newVisitUpdate' => ['newVisitUpdate', 'https://shlink.io/new-visit'];
|
||||
yield 'newShortUrlVisitUpdate' => ['newShortUrlVisitUpdate', 'https://shlink.io/new-visit/foo'];
|
||||
yield 'newVisitUpdate' => ['newVisitUpdate', 'https://shlink.io/new-visit', 'the cool title'];
|
||||
yield 'newShortUrlVisitUpdate' => ['newShortUrlVisitUpdate', 'https://shlink.io/new-visit/foo', null];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user