# `MishkaCmsMigration.Targets.Helpers.BySlug`
[🔗](https://github.com/mishka-group/mishka_cms_migration/blob/v0.0.1/lib/mishka_cms_migration/targets/__helpers/by_slug.ex#L5)

The `existing/2` (`MishkaCmsMigration.Target`) of a target whose resource keeps one record per
slug on a site, such as the Blog's tags and categories: an item whose slug the run's site already
holds a record with, in any language, as the Blog keeps one slug per site, waits for an admin's
decision as `"slug_taken"` (`MishkaCmsMigration.MigrationItem`'s Decide…). Connect to the existing
record connects the item to it (`"keep"`), and Import as new with another slug (`"import"`) writes
it as its own, with the slug `wanted/2` makes and the report saying so (`note/2`,
`"another_slug"`).

The slug is made the way the resource stores it. A WordPress slug arrives decoded, and the
resource's `:create` runs its `slugify` over what it is given, so an item with the slug
`"وردپرس"` is the record with the slug `"wrdprs"`. The slugify options are read from the create
action itself (`MishkaCmsCore.Runtime.Helpers.slugifiers/1`), so a resource that slugifies
otherwise is matched as it is written.

A target whose records are found so, such as the Blog's categories and tags, says
`use MishkaCmsMigration.Targets.Helpers.BySlug` after `use MishkaCmsMigration.Target`: its
`existing/2` is `call/3`, its `note/2` says what `note/2` says, and its `attrs/2` writes the slug
`wanted/2` makes.

The records are read by `MishkaCmsMigration.Targets.Helpers.Read`. `slug/2` is that slug alone,
for a target that points at a record by the address its slug makes, such as a post's link to
another post, or the slug another resource's action stores in a field of its own, such as the
name of a media folder.

# `call`

```elixir
@spec call(
  module(),
  [MishkaCmsMigration.Source.Item.t()],
  MishkaCmsMigration.Target.context()
) :: %{
  required(String.t()) =&gt; MishkaCmsMigration.Target.found()
}
```

What the run's site holds of `items` by slug, by source id: the record an admin decided to
connect an item to, or the conflict an item waits on; an item whose slug the site does not hold,
or that an admin decided to import as new, is left out, to be created.

# `note`

```elixir
@spec note(MishkaCmsMigration.Source.Item.t(), MishkaCmsMigration.Target.context()) ::
  String.t() | nil
```

What the report says of `item` (`MishkaCmsMigration.Target`'s `note/2`): `"another_slug"` once
an admin decided to import it as new with another slug (`wanted/2`), else `nil`.

# `slug`

```elixir
@spec slug(module() | {Ash.Resource.t(), atom(), atom()}, String.t() | nil) ::
  String.t() | nil
```

The slug `target`'s resource stores for a source's `slug`, made as its create action makes it:
`"وردپرس"` is `"wrdprs"` for the Blog's tags; `nil` for no slug. Given
`{resource, action, field}` instead of a target, the slug that action of that resource stores in
that field, such as `{MishkaCmsCore.Runtime.MediaCategory, :create, :name}` for a media folder's
name.

# `wanted`

```elixir
@spec wanted(MishkaCmsMigration.Source.Item.t(), MishkaCmsMigration.Target.context()) ::
  String.t() | nil
```

The slug a target writes `item` with: the source's `"slug"`, or, once an admin decided to import
the item as new beside the site's record of that slug (`"slug_taken"`), that slug with
`-<source id>` after it: `elixir` is `elixir-10`. The resource's create makes it as it makes any
slug, so the Blog's rule holds for it too: `وردپرس-20` is `wrdprs-20`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
