# `Bylaw.Ecto.Query.Checks.NamedBindings`
[🔗](https://github.com/ryanzidago/bylaw/blob/v0.1.0-alpha.1/lib/bylaw/ecto/query/checks/named_bindings.ex#L1)

Validates that an `Ecto.Query` uses named binding aliases in query expressions.

This check reads the prepared Ecto query struct. It requires the root binding
and every join to declare an `:as` alias, then rejects field references that
target bindings without a declared alias.

Ecto expands named binding lists such as `[post: p]` to the same prepared
query shape as local binding variables such as `post`. Because that source
syntax is no longer distinguishable from the prepared query struct, this
check accepts both forms as long as the referenced binding has an alias.

Association join sources, joined preloads, and whole-binding selects are not
rejected because Ecto either requires binding variables for those forms or
erases the source syntax before this check runs.

Ecto's repo lookup helpers, such as `Repo.get_by/3`, generate rootless
keyword `where` queries inside Ecto repo internals before
`c:Ecto.Repo.prepare_query/3` runs. The original caller did not have a place
to provide a root `:as` alias in that form, so this check ignores that
generated lookup shape. Predicate-oriented checks can still validate those
generated `where` fields.

## Examples

Bad:

    from(Post, as: :post)
    |> join(:inner, [post: p], c in assoc(p, :comments))
    |> where([p], p.organization_id == ^organization_id)

Why this is bad:

The join binding has no `:as` alias, and the predicate relies on positional
binding access. As the query grows, it becomes easier to reference the wrong
binding.

Better:

    query =
      from(Post, as: :post)
      |> join(:inner, [post: p], c in assoc(p, :comments), as: :comment)
      |> where([post: p], p.organization_id == ^organization_id)

    Bylaw.Ecto.Query.Checks.NamedBindings.validate(:all, query, [])

Why this is better:

Field references are tied to explicit binding names instead of binding
positions.

## Notes

Ecto's prepared query struct erases some source syntax. This check accepts
named binding lists and local binding variables when the referenced binding
has an alias.

## Options

  * `:validate` - explicit `false` disables the check. Defaults to `true`.

## Usage

Add this module to the explicit check list passed through `Bylaw.Ecto.Query`.
See `Bylaw.Ecto.Query` for the full `c:Ecto.Repo.prepare_query/3` setup.

# `validate`

```elixir
@spec validate(
  Bylaw.Ecto.Query.Check.operation(),
  Bylaw.Ecto.Query.Check.query(),
  opts()
) ::
  Bylaw.Ecto.Query.Check.result()
```

Implements the `Bylaw.Ecto.Query.Check` validation callback.

---

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