Онлайн покупки — это форма электронной коммерции, которая позволяет клиентам напрямую покупать товары или услуги, такие как электронные книги, программное обеспечение и потоковое мультимедиа через Интернет, используя веб-браузер. Этот вид покупок является частью нашей повседневной жизни и используется многими известными сайтами, такими как Amazon, E-bay или различными потоковыми медиа / образовательными сайтами.
Из этой серии будет построен интернет-магазин с нуля. Мы узнаем, как использовать Rails с некоторыми замечательными инструментами, такими как:
- Фонд для разработки адаптивных страниц
- Redis для быстрого хранения товаров в корзине
- Braintree принимать платежи и предоставлять подписки премиум-плана.
Давайте начнем!
Основные характеристики
- Адаптивные страницы: приложение будет построено с использованием адаптивной структуры, чтобы обеспечить оптимальный просмотр для различных устройств.
- Корзина: Позвольте пользователям накапливать список товаров для покупки во время навигации по сайту, затем подсчитать общую сумму заказа перед оформлением заказа.
- Простой сценарий оплаты: принимайте платежи с кредитных карт пользователя напрямую, не сохраняя их на свой торговый счет.
- Сохраните данные кредитной карты: пользователи могут связать свои кредитные карты со своими счетами, чтобы им не нужно было добавлять данные карты каждый раз, когда они хотят что-то купить.
- Управление кредитными картами. Расширьте возможности процесса оплаты, предоставив возможность добавлять и удалять кредитные карты и выбирать между ними при оформлении покупки.
- Планы подписки. Предоставьте пользователям несколько планов подписки, позволяя им обновлять, понижать версию и отменять указанные подписки.
Необходимые навыки
- Хороший опыт работы с Ruby on Rails.
- Средний уровень HTML / CSS3.
- Базовые знания JS / Coffescript & AJAX
- Понимание концепции адаптивного CSS.
- Вводные знания о Redis, магазине ключей-значений.
- Обзор работы онлайн платежных систем.
инструменты
- Ruby 2.1.0 — язык программирования
- Рельсы — Внутренний каркас
- Основа 5 — Фронт-энд CSS рамки
- Devise — аутентификация пользователя
- Redis — Магазин ключей
- Брейнтри — Платежная система
Примечание . Исходный код доступен в этом репозитории Github .
Шаг 1: инициализировать проект
Создать проект
Давайте создадим новое Rails-приложение и скаффолд. Мы используем MySQL в качестве нашей базы данных:
$ rails new moviestore -d mysql $ cd moviestore
подмости
Создайте каркас movie
с некоторыми основными атрибутами модели.
$ rails g scaffold movie title release_year:integer price:float description:text imdb_id poster_url --skip-stylesheets
Здесь мы использовали --skip-stylesheets
со скаффолдом, чтобы избежать генерации каких-либо таблиц стилей, учитывая, что мы будем использовать Foundation.
Теперь мы можем создавать и переносить нашу базу данных.
$ rake db:create $ rake db:migrate
Файл семян
Нам нужны некоторые данные для работы, поэтому я создал CSV-файл с именем movies.csv
который содержит некоторые записи фильмов. Я положил его в каталог db/seeds_data
.
Мы пройдемся по записям в файле csv, чтобы загрузить его строки в нашу базу данных.
require 'csv' CSV.foreach(Rails.root.join("db/seeds_data/movies.csv"), headers: true) do |row| Movie.find_or_create_by(title: row[0], release_year: row[1], price: row[2], description: row[3], imdb_id: row[4], poster_url: row[5]) end
После этого вы можете запустить файл rake
с помощью rake
:
$ rake db:seed
Конфигурация маршрутов
Корневой маршрут
Пока что, сделайте корневой маршрут, чтобы перейти к действию index
контроллера фильмов:
root 'movies#index'
Ресурсы ОТДЫХА
Нам не нужны все конечные точки, предоставляемые resource
, такие как create
, update
и т. Д. Ограничьте доступные действия index
и show
:
resources :movies, only: [:show, :index]
Также удалите все ненужные файлы в представлениях и ненужный код в MoviesController
.
Завершить настройку
Наконец, запустите rails s
и откройте localhost: 3000 в браузере, и вы увидите наш список фильмов.
Отлично сработано! мы только что завершили первоначальную настройку проекта, но стиль отсутствует. Давайте добавим немного хорошего стиля, используя Zurb Foundation.
Шаг 2: Адаптивный дизайн с Фондом
Foundation — это интерфейсный фреймворк, похожий на Twitter Bootstrap, он построен на SASS вместо LESS, что облегчает его интеграцию в приложение Rails.
Установка
Чтобы установить Foundation, добавьте эту строку в Gemfile приложения:
gem 'foundation-rails'
а потом:
$ bundle install
Конфигурирование Фонда
Выполните следующую команду, чтобы добавить основание:
$ rails g foundation:install
Это создаст несколько файлов, необходимых для Foundation, включая файл макета, который спросит вас, хотите ли вы перезаписать существующий файл макета. Ты сделаешь.
Подсказка Вам необходимо перезапустить сервер после добавления основного камня.
Переопределить Фонд
Если вы сейчас посмотрите на файл application.css , то заметите, что для него требуется файл foundation_and_overrides
, созданный генератором фундаментов. Это позволяет нам настраивать Фонд, устанавливая переменные. Он содержит множество комментариев, показывающих нам, какие переменные мы можем настроить.
Foundation Icon Fonts
Естественно, нам понадобится набор иконок для добавления значимых символов в наш дизайн. К счастью, Zurb Foundation предоставляет шрифты значков, которые вы можете скачать здесь . Добавьте его в свое приложение, скопировав папку fonts в папку app / assets и переместив файл foundation-icons.css в папку app / assets / stylesheets .
Настроить макет
На данный момент макет выглядит нормально, но мы можем многое сделать для его улучшения. Мы добавим некоторую структуру на страницу и добавим заголовок, логотип, нижний колонтитул и основной контент.
Чтобы сделать это быстро, возьмите один из HTML-кода шаблона Foundation и поместите его между теги нашей страницы макета. Это очень просто в использовании.
Шаблон приложения
После некоторых настроек одного из шаблонов Foundation макет теперь состоит из 4 основных частей (верхняя панель, оповещения, основное содержимое и нижний колонтитул):
<body> <%= render "layouts/header" %> <%= render "layouts/alerts" %> <div id="main-content" class="row full-min-height"> <%= yield %> </div> <footer class="row"> <div class="large-12 columns"> <hr> <p>© MovieStore 2014</p> </div> </footer> <%= javascript_include_tag "application" %> </body>
У нас есть две части, чтобы помочь организовать макет. Первый - _header
, который содержит меню верхней панели:
<div class="row"> <nav class="top-bar column" data-topbar> <ul class="title-area"> <li class="name"> <h1> <a href="/"> <i class="fi-play-video"></i> MovieStore </a> </h1> </li> <li class="toggle-topbar"><a href="#"><span>Menu</span></a></li> </ul> <section class="top-bar-section"> <ul class="right"> <li><%=link_to "Register", "#" %></li> <li><%=link_to "Login", "#" %></li> </ul> </section> </nav> </div>
Второй - _alerts
, который отображает любые флеш-уведомления или уведомления:
<%if notice.present? || alert.present? %> <div class="row"> <%is_alert = alert.present? ? "alert" : ""%> <div data-alert class="alert-box <%=is_alert%>"> <%=notice || alert%> <a href="#" class="close">×</a> </div> </div> <%end%>
Настроить CSS
CSS Helpers Classes
Создайте новый файл с именем helpers.scss в каталоге app / stylesheets . В этом файле мы определим некоторые вспомогательные классы для хранения наиболее используемого CSS в нашем приложении. Цель этих классов - сделать ваш код многократно используемым, более быстрым и эффективным. Это один из вариантов OOCSS , который я считаю хорошим CSS. Если вы раньше не читали об OOCSS (объектно-ориентированном CSS), я призываю вас сделать это!
Вот таблица стилей помощников .
Базовые переменные
Как объяснялось ранее, мы настроим некоторые переменные Foundation для изменения цветов и стилей по умолчанию, таких как кнопки, метки, входные данные и т. Д. Лучший способ узнать, какие переменные вам нужно изменить, можно найти в документах Foundation. В конце каждого раздела показаны соответствующие переменные и способы их настройки. Найдите эти переменные внутри foundation_overrides.scss и измените их на нужные значения. Вы можете найти мои собственные переменные здесь .
Пользовательский стиль макета
Мы хотим добавить больше настроек в макет. Для этого создайте файл layout.scss и добавьте следующее:
// Custom layout styles // Default font family @font-face { font-family: 'gotham-rounded'; src: url(gotham-rounded-medium.otf); } // Body background body { background-image: url(body_bg.png); background-position: center; color: #fff; } // Logo .title-area{ a { padding:0 !important } i { font-size: 30px; position: relative; top: 2px; } } // Topbar items .top-bar-section ul li { background: transparent } // Forms .form-container { padding: 3% 3%; .switch > div { position: relative; top: -5px; } }
Страницы стиля
Главная страница
Использование вида сетки будет самым простым решением для отображения фильмов. Мы можем сделать это, используя основную сеточную систему, которая дает вам возможность равномерно разделить содержимое списка в сетке. Давайте настроим страницу index
и добавим классы сетки:
<h4 class="column">Featured Movies</h4> <div class="column"> <ul class="movies-grid no-bullet row"> <% @movies.each do |movie| %> <li class="large-3 medium-4 small-12 column"> <div class="movie-card padly"> <%= link_to movie, class: "poster" do %> <%= image_tag movie.poster %> <% end %> <div class="movie-info ell glassy-bg padmy padlx"> <div class="title"> <h6><%= movie.title %> <span>(<%= movie.release_year %>)</span></h6> </div> <p class="left price label movie-label radius">$ <%= movie.price %></p> <%= link_to movie.imdb, class: "right" do %> <%= image_tag asset_path("imdb_logo.png") %> <% end %> </div> </div> </li> <% end %> </ul> </div>
Основа Grid System основана на двенадцати столбцах, и мы определяем их с помощью CSS-классов, поэтому вы можете быстро и легко создавать мощные макеты для нескольких устройств с помощью стандартной 12-колоночной несущей сетки.
Мы также добавили некоторые из наших собственных классов CSS, чтобы улучшить дизайн и исправить его ошибки:
.movies-grid { .movie-card { width:220px; margin:auto; .poster img { height: 325px; width:100%; } .movie-info { .title { height: 4em; } } } }
Обратите внимание, мы называем imdb
и poster
на @movie
которые не являются атрибутами в модели Movie
. Это потому, что я создал два метода экземпляра, которые возвращают соответствующие URL на основе других атрибутов.
class Movie < ActiveRecord::Base def poster "http://ia.media-imdb.com/images/M/#{poster_url}" end def imdb "http://www.imdb.com/title/#{imdb_id}/" end end
Показать страницу
Последняя задача, связанная со стилем, - это редизайн страницы show
на основе нашего нового макета и классов:
<div class="large-3 small-12 column"> <%=image_tag @movie.poster%> </div> <div class="large-9 small-12 column"> <h3> <%= @movie.title %> (<%= @movie.release_year %>) <%=link_to @movie.imdb do%> <%=image_tag asset_path("imdb_logo.png")%> <%end%> </h3> <p class="label movie-label radius mb1">$ <%= @movie.price %></p> <p><%= @movie.description %></p> </div>
Отлично! Теперь вы понимаете, как работать с Foundation, как его переопределить и как добавлять свои собственные таблицы стилей.
Шаг 3: Разработка пользовательской модели (аутентификация)
Чтобы обрабатывать аутентификацию в нашем приложении, мы должны создать модель User
с простыми формами регистрации и авторизации. Разработать это один из самых простых способов сделать это.
Установить Devise
Сначала, как мы использовали, добавьте devise gem в ваш Gemfile и выполните команду bundle
чтобы установить его:
gem 'devise'
после установки Devise запустите генератор, чтобы создать инициализатор devise.rb, который описывает параметры конфигурации Devise:
$ rails generate devise:install
Добавить модель пользователя
Создайте модель User
и свяжите ее с Devise, используя ее генератор:
$ rails generate devise User
Это создаст модель User
и настроит ее с модулями Devise по умолчанию. Генератор также настраивает ваш файл config / rout.rb так, чтобы он указывал на контроллер Devise.
$ rake db:migrate
Настройка маршрутов
Devise предоставляет метод devise_for
в нашем файле devise_for
, что позволяет нам настраивать имена путей для разработки маршрутов следующим образом:
devise_for :users, path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' }
Настройка представлений
Devise - это законченное MVC-решение на основе движков Rails, основанное на модульной концепции: «используйте то, что вам действительно нужно».
Как вы могли заметить, для Devise нет сгенерированных представлений. Это потому, что взгляды Devise упакованы в жемчужине. Нам нужно только сгенерировать несколько наборов представлений (например, формы register
и login
) и настроить их. Запустите следующий генератор и передайте ему список этих модулей с флагом -v
и он скопирует выбранные представления в ваше приложение.
$ rails generate devise:views -v registrations sessions
Хорошо! Теперь мы можем изменить формы регистрации и входа в систему с помощью нашего индивидуального макета.
Форма регистрации
просмотры / регистрация / new.html.erb
<div class="form-container radius-box glassy-bg small-10 small-centered medium-8 large-6 columns"> <h2>Register</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> <div class="mb1"><%= f.email_field :email, autofocus: true, placeholder: "Email", class: "radius" %></div> <div class="mb1"><%= f.password_field :password, autocomplete: "off", placeholder: "Password", class: "radius" %></div> <div class="mb1"><%= f.password_field :password_confirmation, autocomplete: "off", placeholder: "Confirm password", class: "radius" %></div> <div><%= f.submit "Let's Go", class: "button" %></div> <% end %> <%= render "devise/shared/links" %> </div>
Форма входа
просмотры / сессия / new.html.erb
<div class="form-container radius-box glassy-bg small-10 small-centered medium-8 large-6 columns"> <h2>Login</h2> <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <div class="mb1"><%= f.email_field :email, autofocus: true, placeholder: "Email", class: "radius" %></div> <div class="mb1"><%= f.password_field :password, autocomplete: "off", placeholder: "Password", class: "radius" %></div> <% if devise_mapping.rememberable? -%> <div class="switch round small mb1"> <div class="inline-block left prm"><%= f.check_box :remember_me %> <%= f.label :remember_me %></div> <span>Remember me</span> </div> <% end -%> <div class="clear"><%= f.submit "Let me in", class: "button" %></div> <% end %> <%= render "devise/shared/links" %> </div>
Раздел пользователя Topbar
Поскольку теперь у нас есть вошедшие и вышедшие пользователи, отредактируйте раздел topbar внутри файла layout / _header.html.erb, чтобы показать правильные ссылки в зависимости от Devise signed_in?
помощник.
<ul class="right"> <%if signed_in?%> <li><%=link_to current_user.email, edit_user_registration_path%></li> <li><%=link_to "Logout", destroy_user_session_path, method: :delete%></li> <%else%> <li><%=link_to "Register", new_user_registration_path%></li> <li><%=link_to "Login", new_user_session_path%></li> <%end%>
Шаг 4: Корзина
На этом этапе мы хотим добавить корзину покупок, чтобы пользователи могли добавлять фильмы, которые они хотят купить, в свои тележки. У них также будет возможность посетить страницу корзины и просмотреть фильмы, которые они добавили в корзину, удалив при желании любой из них.
Для реализации этого сценария есть несколько полезных камней, таких как carter & act как shopping_cart , но я предпочитаю создавать его с нуля, используя Redis Redis очень быстрый и имеет некоторые функции, которые идеально соответствуют нашим потребностям.
Redis - это расширенное хранилище ключей в памяти с открытым исходным кодом. Его часто называют сервером структуры данных, поскольку ключи могут содержать строки, хэши, списки, наборы и отсортированные наборы.
Настройка Redis
Установка
Если вы используете OS X, самый простой способ установить Redis - это использовать Homebrew ( brew install redis
).
Затем добавьте следующие гемы в наш Gemfile и запустите bundle
gem 'redis', '~> 3.0.1' gem 'hiredis', '~> 0.4.5'
По умолчанию redis
использует библиотеку сокетов Ruby для общения с Redis. Поскольку у нас будет несколько больших ответов (например, SMEMBERS
списка SMEMBERS
которая будет использоваться в нашем приложении), мы используем альтернативный драйвер соединения, называемый hiredis
который оптимизирует скорость за счет переносимости.
Наконец, мы можем запустить его, выполнив следующую команду:
$ redis-server
Инициализировать глобальный объект
Создайте файл инициализатора с именем /config/initializers/redis.rb, где мы настроим наше соединение Redis. Будет создана глобальная переменная, чтобы обеспечить легкий доступ к остальной части нашего приложения. Также укажите hiredis
в качестве драйвера при создании экземпляра объекта client.
$redis = Redis.new(:driver => :hiredis)
Ницца! Теперь мы успешно настроили Redis, и он готов к использованию в нашем приложении.
Тележка Контроллер
Сначала создайте контроллер тележек, передавая show
как параметр, потому что это единственное действие, которое мы будем использовать.
$ rails g controller carts show
Маршруты
Поскольку мы имеем дело с одной корзиной на пользователя, cart
может быть единственным ресурсом . Добавьте only
параметр с действием show
и определите два не RESTful-маршрута для действий add
и remove
.
resource :cart, only: [:show] do put 'add/:movie_id', to: 'carts#add', as: :add_to put 'remove/:movie_id', to: 'carts#remove', as: :remove_from end
Действия контроллера
Внутри CartsController
мы будем использовать команды SMEMBERS
, SADD
и SREM
Redis для перечисления, добавления и удаления идентификаторов фильмов в уникальный набор с именем current_user_cart
, идентифицируемый с помощью current_user.id
. Для получения более подробной информации о командах Redis я рекомендую вам ознакомиться с их документацией , она очень понятна и полезна.
class CartsController < ApplicationController before_action :authenticate_user! def show cart_ids = $redis.smembers current_user_cart @cart_movies = Movie.find(cart_ids) end def add $redis.sadd current_user_cart, params[:movie_id] render json: current_user.cart_count, status: 200 end def remove $redis.srem current_user_cart, params[:movie_id] render json: current_user.cart_count, status: 200 end private def current_user_cart "cart#{current_user.id}" end end
Мы добавили :authenticate_user!
в качестве обратного вызова before_action
для ограничения доступа к действиям контроллера только зарегистрированным пользователям. Кроме того, в действиях добавления и удаления мы отвечаем current_user.cart_count
который является методом экземпляра в модели User
который возвращает количество фильмов в корзине текущих пользователей с помощью команды SCARD
:
def cart_count $redis.scard "cart#{id}" end
Последнее, что нужно сделать, это добавить ссылку « My Cart
в меню верхней панели, чтобы пользователи могли получить к ней прямой доступ в любое время. Также,
давайте представим количество фильмов в их тележках:
<li> <%= link_to cart_path do%> <i class="fi-shopping-cart"></i> My Cart (<span class="cart-count"><%=current_user.cart_count%></span>) <%end%> </li>
Кнопка «Добавить в корзину»
Создайте кнопку «Добавить в корзину», которая обрабатывает действия add
и remove
. Мы добавим его на страницу show
фильмов:
<%if signed_in?%> <%=link_to "", class: "button", data: {target: @cart_action, addUrl: add_to_cart_path(@movie), removeUrl: remove_from_cart_path(@movie)} do%> <i class="fi-shopping-cart"></i> <span><%=@cart_action%></span> Cart <%end%> <%end%>
Мы определяем @cart_action
экземпляра @cart_action
в фильме movies#show
action как:
def show @movie = Movie.find(params[:id]) @cart_action = @movie.cart_action current_user.try :id end
Он вызывает метод @movie
, который проверяет, является ли этот фильм участником корзины текущего пользователя. Он возвращает соответствующий текст, который будет отображаться как метка кнопки, а также использоваться в нашем coffescript, как мы увидим позже.
def cart_action(current_user_id) if $redis.sismember "cart#{current_user_id}", id "Remove from" else "Add to" end end
Здесь мы использовали команду SISMEMBER
Redis, которая определяет, является ли данное значение членом набора.
Добавить / удалить запросы
Нам нужно подключить кнопку «Добавить в корзину», используя некоторые скрипты jQuery
и запросы AJAX.
$(window).load -> $('a[data-target]').click (e) -> e.preventDefault() $this = $(this) if $this.data('target') == 'Add to' url = $this.data('addurl') new_target = "Remove from" else url = $this.data('removeurl') new_target = "Add to" $.ajax url: url, type: 'put', success: (data) -> $('.cart-count').html(data) $this.find('span').html(new_target) $this.data('target', new_target)
Мы проверяем состояние текущего фильма с помощью target
атрибута данных, который имеет значение @cart_action
как мы упоминали ранее. Следовательно, мы можем указать url
ajax, который является либо add_to_cart
либо remove_from_cart
, так как мы передаем их в наш скрипт, используя addurl
данных addurl
& removeurl
.
После успешного выполнения ajax-запроса установите количество фильмов в корзине, которое находится в верхнем меню, с новым номером, измените метку кнопки и установите target
атрибут данных с новым значением.
Примечание : я отключил
Turbolinks.js
чтобы мы могли сделать наш скрипт максимально простым. Вы можете сделать это, просто удаливrequire turbolinks
из файла application.js .
Страница моей корзины
Теперь, последнее, что нужно сделать, это представить элементы carts/show
странице carts/show
. Давайте добавим несколько тегов HTML:
<div id="mycart" class="small-10 small-centered medium-8 large-8 column"> <div class="p1 glassy-bg mb1 text-center radius-l1 radius-r1"> <h4>My Cart</h4> <p class="mb0"> You've selected <span class="cart-count"><%=current_user.cart_count%></span> movies!</p> </div> <% @cart_movies.each do |movie|%> <div data-equalizer class="cart-movie large-12 column mb1"> <div class="column large-2 text-center p0" data-equalizer-watch> <%=link_to movie do%> <%=image_tag movie.poster, class: "radius-l1"%> <%end%> </div> <div class="column large-7 glassy-bg text-center" data-equalizer-watch> <p class="scale ptm"> <%= movie.title %> </p> </div> <div class="column large-3 primary-bg text-center radius-r1" data-equalizer-watch> <%=link_to "" , data: {targetUrl: remove_from_cart_path(movie)} do%> <i class="fi-x right"></i> <%end%> <h4 class="scale">$ <%= movie.price %></h4> </div> </div> <%end%> </div>
Эквалайзер , предоставляемый Фондом, является отличным способом для создания контента равной высоты на вашей странице. Вы можете найти более подробную информацию о том, как это работает здесь .
Добавьте немного особого стиля в файл carts.scss :
#mycart { .scale { position: relative; top: 35%; transform: translateY(-35%); } .fi-x { position: relative; top: 10px; right: -5px; } }
Наконец, напишите аналогичный скрипт для активации функции remove
значка.
$(window).load -> $('#mycart .fi-x').click (e) -> e.preventDefault() $this = $(this).closest('a') url = $this.data('targeturl') $.ajax url: url, type: 'put', success: (data) -> $('.cart-count').html(data) $this.closest('.cart-movie').slideUp()
Вау, кажется, что сейчас все работает гладко. Прекрасная работа!
Еще больше делать
Еще предстоит еще много работы, прежде чем мы сможем сказать, что наш интернет-магазин завершен. В моей следующей статье я расскажу об интеграции Braintree с нашим приложением для приема платежей. Спасибо за прочтение!