-
Notifications
You must be signed in to change notification settings - Fork 53
Extension Approach 2
This approach of adding Nectar Extensions and building the Use Store is built around having having nectar, extensions and user store application in a single umbrella project
-
Nectar
- Provides the E-commerce model and resources (This is the core code which forms the nectar commerce framework). Modification to this code should be avoided.
-
ExtensionManager
(will be a part of default install, meant to be modifiable by user)- Provides a basic DSL and install point for 3rd party extensions to use
- Add the third party extensions as dependencies and follow their install instructions.
-
Extensions
- 3rd Party code to add / enhance Nectar feature, for example wallet or wishlist.
- They can be anything ranging from a payment gateway to a full fledged phoenix app.
-
User Store
- The launch point for the application, all apps that need to be run when starting the store need to present here.
- User code goes here as well as a root forward to nectar. Please see the example code for this.
Any modification to core code should be preferably done only via the Extensions
application and modifying nectar commerce source should be avoided to maintain compatibility with future releases.
-
User Store
depends uponNectar
+Extensions
which need to run. Not all extensions need to run, some may just modify nectar at compile time and may have no role at runtime. -
ExtensionManager
depends uponExtensions
that need to modify nectar core. -
Extensions
may themselves rely on nectar if needed, this is only required if some component of nectar is required in the extension at compile time. for example they may want to do so to use nectar models in pattern matching or reusing Nectar.Web in their own phoenix apps. For all other cases aliases and direct references should be sufficient. -
Note: reusing Nectar.Web, also has the sometimes useful side effect that
Extensions
themselves become open for extension by otherExtensions
.
Regular Mix project with
- Special macros to provide a DSL to extend schema, inject functions in Nectar Models and add routes in Nectar Router
- Help consolidate extensions to be available to extend Nectar.
- Knowledge of all registered extensions.
- Should always be present in umbrella, but is not required/loaded at runtime.
-
lib/extensions_manager/model_extension.ex
-
Provides a DSL to extend Model schema present in Nectar App and inject support functions in Model
-
Model Schema extension
-
add_to_schema(schema_change, name, type, options)
-
schema_change can be a field in Model or an association
add_to_schema(:has_many, :liked_by, through: [:likes, :user]) add_to_schema(:has_many, :likes, FavoriteProducts.UserLike, [])
-
-
Inject Model functions
include_method do def liked_products(model) do from like in assoc(model, :likes), preload: [:liked_products] end end
-
-
lib/extensions_manager/router_extension.ex
-
Provides a DSL to add routes in Nectar App as needed by extension
```elixir define_route do scope "/", FavoriteProducts do pipe_through [:browser, :browser_auth] # Use the default browser stack resources "/likes", FavoriteController, only: [:index, :create, :delete] end end ```
-
-
lib/extensions_manager/install_extensions.ex
-
Provides an interface for extensions to talk to Nectar and convey their requests of schema_changes and function_inclusions
-
Provides an interface for extensions to add routes in Nectar Router
defmodule ExtensionsManager.ExtendProduct do use ExtensionsManager.ModelExtension use FavoriteProducts.NectarExtension, install: "products" end defmodule ExtensionsManager.ExtendUser do use ExtensionsManager.ModelExtension # note multiple extensions can be installed via this process. use FavoriteProducts.NectarExtension, install: "users" end defmodule ExtensionsManager.Router do use ExtensionsManager.RouterExtension use FavoriteProducts.NectarExtension, install: "router" end
-
-
mix.exs
-
add extensions as dependency in
deps
defp deps do [{:favorite_products, in_umbrella: true}] end
-
Note: The naming convention in install.ex is utilized by Nectar to search for modules to add. Please see the convention document for more details
Changes in Nectar to enable and hook the extensions through Extensions
Application
-
-
model definition is enhanced to include
use Nectar.Extender
to enable the discovery of extensions during compilation. this module uses Code.ensure_loaded to load the extension into memory if available instead of acquiring it via a dependency.defmodule Nectar.Web do def model do quote do ... use Nectar.Extender ... end end end
-
-
Schema block/definition in all models will have
extensions
function to help extend schema from extensions. This only allows for adding to schema right now.defmodule Nectar.Product do ... schema "products" do ... extensions ... end ... end
-
-
uses Nectar.RouteExtender to search for and add the extension routes into the nectar router.
defmodule Nectar.Router do ... use Nectar.RouteExtender end
-
This Extension is created as a regular Phoenix Apps to provide additional views and models to Nectar along with an extra module in lib/favorite_products/nectar_extension.ex
which defines model and route extension install steps using the DSL provided by ExtensionManager
application.
All the other files as needed in model, controller, views, templates, migrations are added & worked upon as in any regular phoenix project
Favorite Products Extension needs
- Model
- a join table to store user favorited products
- put in
web/models
- Migration to create new joins table with user_id and product_id
- put in
priv/repo/migrations
- put in
- Product and User Model Schema Extension to inject associations to be aware of likes_join table
- Refer here
- Ensure the database configured is same in nectar and the extensions.
- User Facing Favorite Controller
- shows user favorite products
- allows mark / unmark product as favorite
- put in
web/controllers
,web/views
,web/templates
- Admin Facing Favorite Controller
- show all favorited products
- shows users who favorited particular product
- put in
web/controllers/admin
,web/views/admin
,web/templates/admin
- Routes to Expose User / Admin Facing Favorite Controller
- Refer here
- Extension must define the install steps for:
- schema_changes
- support functions
- routes to be registered with full scope mentioning pipelines to be used
- Don't forget to include
favorite_products
as umbrella dependency toextensions
# lib/favorite_products/nectar_extension.ex
# Add it next to endpoint.ex under phoenix app lib folder
defmodule FavoriteProducts.NectarExtension do
# note there will be a convention/dsl for writing this file
# to make it more readable and uniform across all extensions
defmacro __using__([install: install_type]) do
do_install(install_type)
end
defp do_install("router") do
quote do
define_route do
scope "/", FavoriteProducts do
## Do not forget to add the pipelines request should go through
pipe_through [:browser, :browser_auth]
resources "/favorites", FavoriteController, only: [:index, :create, :delete]
end
scope "/admin", FavoriteProducts.Admin, as: :admin do
## Do not forget to add the pipelines request should go through
## Note the `admin_browser_auth` pipeline instead of `browser_auth`
pipe_through [:browser, :admin_browser_auth]
resources "/favorites", FavoriteController, only: [:index]
end
end
end
end
defp do_install("products") do
quote do
## In Phoenix App Model Schema definition, join association is defined first and then through association
## Please note the reverse order here as while collecting, it gets the reverse order and
## injected into actual model schema as expected .. tricky huhh !!
add_to_schema(:has_many, :liked_by, through: [:likes, :user])
add_to_schema(:has_many, :likes, FavoriteProducts.UserLike, [])
include_method do
def like_changeset(model, params \\ :empty) do
model
|> cast(params, ~w(), ~w())
|> cast_assoc(:likes) # will be passed the user id here.
end
def liked_by(model) do
from like in assoc(model, :liked_by)
end
end
end
end
defp do_install("users") do
quote do
add_to_schema(:has_many, :liked_products, through: [:likes, :product])
add_to_schema(:has_many, :likes, FavoriteProducts.UserLike, [])
include_method do
def liked_products(model) do
from like in assoc(model, :liked_products)
end
end
end
end
end
Run mix clean && mix compile
umbrella root and then start the phoenix server from iex -S mix phoenix.server
from User Store
app. Note there will a second round of compilation at this point which loads the extensions into nectar. The compile process will need to be repeated after adding any new extension to the umbrella.
-
Confirming Model Schema Extension, associations in the IEx shell with project loaded.
alias Nectar.Repo alias Nectar.User alias Nectar.Product # Add an entry in user_likes join table manually for valid product and user # Change the User and Product id as available in DB u = Repo.get(User, 1) |> Repo.preload([:liked_products]) p = Repo.get(Product, 1) |> Repo.preload([:liked_by])
-
Hit through Browser
http://localhost:4000/favorites
iex(4)> [info] GET /favorites [debug] Processing by FavoriteProducts.FavoriteController.index/2 Parameters: %{} Pipelines: [:browser, :browser_auth] [info] Sent 200 in 329µs
##Refer working favorite_products extension code here##
##Installation Instructions##
- The current code base is configured to use postgres. Please see the adapter configuration in /apps/favorite_products/config/dev.exs and /apps/user_app/config/dev.exs more details, Please see here on how to configure nectar.
- Once configuration is complete. run
mix deps.get && mix clean && mix compile
from root of the umbrella project. - Then migrate the code by running
mix ecto.migrate -r Nectar.Repo
thenmix ecto.migrate -r FavoriteProducts.Repo
. - Seed the data with
mix run apps/nectar/priv/repo/seeds.exs
. - Change directory to
cd apps/user_app
and runnpm install
andbower install
. - And start the phoenix server with
mix phoenix.server
. - Visit localhost:4000/favorites to see the extension in action.