[Review] Fixes after PR review

This commit is contained in:
Grzegorz Sadowski 2016-02-16 15:33:53 +01:00
parent a2167e3ae3
commit c9a86d0b83
104 changed files with 1257 additions and 1360 deletions

View file

@ -22,6 +22,7 @@ imports:
- suites/legacy/products.yml
- suites/legacy/promotions.yml
- suites/legacy/reports.yml
- suites/legacy/reviews.yml
- suites/legacy/search.yml
- suites/legacy/security.yml
- suites/legacy/settings.yml

View file

@ -16,5 +16,6 @@ default:
- Sylius\Bundle\TaxonomyBundle\Behat\TaxonomyContext
- Sylius\Bundle\WebBundle\Behat\WebContext
- Sylius\Bundle\ChannelBundle\Behat\ChannelContext
- Sylius\Bundle\ReviewBundle\Behat\ReviewContext
filters:
tags: "@legacy_products"

View file

@ -0,0 +1,20 @@
# This file is part of the Sylius package.
# (c) Paweł Jędrzejewski
default:
suites:
reviews:
contexts:
- Behat\MinkExtension\Context\MinkContext
- Sylius\Bundle\ChannelBundle\Behat\ChannelContext
- Sylius\Bundle\CoreBundle\Behat\CoreContext
- Sylius\Bundle\CoreBundle\Behat\HookContext
- Sylius\Bundle\MoneyBundle\Behat\MoneyContext
- Sylius\Bundle\ProductBundle\Behat\ProductContext
- Sylius\Bundle\ResourceBundle\Behat\FixtureContext
- Sylius\Bundle\ReviewBundle\Behat\ReviewContext
- Sylius\Bundle\TaxationBundle\Behat\TaxationContext
- Sylius\Bundle\TaxonomyBundle\Behat\TaxonomyContext
- Sylius\Bundle\WebBundle\Behat\WebContext
filters:
tags: "@legacy_reviews"

View file

@ -1,87 +0,0 @@
@products
Feature: Products
In order to know and pick the products
As a visitor
I want to be able to browse products
Background:
Given there is default currency configured
And there are following taxonomies defined:
| code | name |
| RTX1 | Category |
And there are following users:
| email | password | enabled |
| bar@foo.com | foo1 | yes |
And taxonomy "Category" has following taxons:
| Clothing[TX1] > T-Shirts[TX2] |
| Clothing[TX1] > PHP T-Shirts[TX3] |
| Clothing[TX1] > Gloves[TX4] |
And there are following channels configured:
| code | name | currencies | locales | url |
| WEB-US | mystore.us | EUR, GBP | en_US | |
| WEB-EU | mystore.eu | USD | en_GB, fr_FR, de_DE | localhost |
And there are following attributes:
| name | type |
| T-Shirt fabric | text |
And the following products exist:
| name | price | taxons | pricing calculator | calculator configuration | attributes | average_rating |
| Super T-Shirt | 19.99 | T-Shirts | channel_based | WEB-EU:15.99 | T-Shirt fabric:Wool | 0 |
| Black T-Shirt | 18.99 | T-Shirts | | | | 0 |
| Sylius Tee | 12.99 | PHP T-Shirts | | | | 0 |
| Symfony T-Shirt | 15.00 | PHP T-Shirts | | | | 5 |
| Doctrine T-Shirt | 15.00 | PHP T-Shirts | | | | 0 |
And channel "WEB-EU" has following configuration:
| taxonomy |
| Category |
And channel "WEB-EU" has following products assigned:
| product |
| Super T-Shirt |
| Symfony T-Shirt |
And channel "WEB-US" has following products assigned:
| product |
| Sylius Tee |
| Black T-Shirt |
| Doctrine T-Shirt |
And there are following reviews:
| title | rating | comment | author | product | subject type |
| Lorem ipsum | 5 | Lorem ipsum dolor sit | bar@foo.com | Symfony T-Shirt | product |
Scenario: Displaying product rating and reviews:
Given I am on the store homepage
And I follow "PHP T-Shirts"
When I click "Symfony T-Shirt"
Then I should see "Rating: 5"
And I should see 1 review on the reviews list
@javascript
Scenario: Adding review as logged in user
Given I am logged in as "bar@foo.com"
And I am on the product page for "Symfony T-Shirt"
When I fill in "Title" with "Very good"
And I fill in "Comment" with "Very good shirt."
And I select the "5" radio button
And I press "Submit"
And I wait 3 seconds
Then I should see "Product review has been successfully created."
@javascript
Scenario: Adding review as guest
Given I am on the product page for "Symfony T-Shirt"
When I fill in "Title" with "Very good"
And I fill in "Comment" with "Very good shirt."
And I fill in "Email" with "guest@example.com"
And I select the "5" radio button
And I press "Submit"
And I wait 3 seconds
Then I should see "Product review has been successfully created."
@javascript
Scenario: Trying to add review as guest
Given I am on the product page for "Symfony T-Shirt"
When I fill in "Title" with "Very good"
And I fill in "Comment" with "Very good shirt."
And I fill in "Email" with "bar@foo.com"
And I select the "5" radio button
And I press "Submit"
And I wait 3 seconds
Then I should see "This email is already registered. Please log in."

View file

@ -0,0 +1,63 @@
@legacy_products
Feature: Product reviews
In order to rate and learn more about the products
As a Visitor
I want to browse and post product reviews
Background:
Given there is default currency configured
And there are following users:
| email | password | enabled |
| bar@foo.com | foo1 | yes |
And there are following channels configured:
| code | name | currencies | locales | url |
| WEB-EU | mystore.eu | USD | en_GB, fr_FR, de_DE | localhost |
And the following products exist:
| name | price | pricing calculator | calculator configuration | attributes |
| Symfony T-Shirt | 15.00 | | | |
And channel "WEB-EU" has following products assigned:
| product |
| Symfony T-Shirt |
And there are following reviews:
| title | rating | comment | author | product | subject type |
| Lorem ipsum | 5 | Lorem ipsum dolor sit | bar@foo.com | Symfony T-Shirt | product |
| Consectetur | 4 | Consectetur adipiscing | bar@foo.com | Symfony T-Shirt | product |
| Proin nibh | 3 | Proin nibh augue | bar@foo.com | Symfony T-Shirt | product |
Scenario: Displaying product rating and reviews:
When I am on the product page for "Symfony T-Shirt"
Then I should see "Rating: 4"
And I should see 3 review on the reviews list
@javascript
Scenario: Adding review as logged in user
Given I am logged in as "bar@foo.com"
And I am on the product page for "Symfony T-Shirt"
When I fill in "Title" with "Very good"
And I fill in "Comment" with "Very good shirt."
And I select the "5" radio button
And I press "Submit"
And I wait 3 seconds
Then I should see "Product review has been successfully created."
@javascript
Scenario: Adding review as guest
Given I am on the product page for "Symfony T-Shirt"
When I fill in "Title" with "Very good"
And I fill in "Comment" with "Very good shirt."
And I fill in "Email" with "guest@example.com"
And I select the "5" radio button
And I press "Submit"
And I wait 3 seconds
Then I should see "Product review has been successfully created."
@javascript
Scenario: Trying to add review as guest
Given I am on the product page for "Symfony T-Shirt"
When I fill in "Title" with "Very good"
And I fill in "Comment" with "Very good shirt."
And I fill in "Email" with "bar@foo.com"
And I select the "5" radio button
And I press "Submit"
And I wait 3 seconds
Then I should see "This email is already registered, please login or use forgotten password."

View file

@ -1,80 +1,80 @@
@legacy_products
Feature: Products
In order to know and pick the products
As a visitor
I want to be able to browse products
In order to know and pick the products
As a visitor
I want to be able to browse products
Background:
Given there is default currency configured
And there are following taxonomies defined:
| code | name |
| RTX1 | Category |
And taxonomy "Category" has following taxons:
| Clothing[TX1] > T-Shirts[TX2] |
| Clothing[TX1] > PHP T-Shirts[TX3] |
| Clothing[TX1] > Gloves[TX4] |
And there are following channels configured:
| code | name | currencies | locales | url |
| WEB-US | mystore.us | EUR, GBP | en_US | |
| WEB-EU | mystore.eu | USD | en_GB, fr_FR, de_DE | localhost |
And there are following attributes:
| name | type |
| T-Shirt fabric | text |
And the following products exist:
| name | price | taxons | pricing calculator | calculator configuration | attributes |
| Super T-Shirt | 19.99 | T-Shirts | channel_based | WEB-EU:15.99 | T-Shirt fabric:Wool |
| Black T-Shirt | 18.99 | T-Shirts | | | |
| Sylius Tee | 12.99 | PHP T-Shirts | | | |
| Symfony T-Shirt | 15.00 | PHP T-Shirts | | | |
| Doctrine T-Shirt | 15.00 | PHP T-Shirts | | | |
And channel "WEB-EU" has following configuration:
| taxonomy |
| Category |
And channel "WEB-EU" has following products assigned:
| product |
| Super T-Shirt |
| Symfony T-Shirt |
And channel "WEB-US" has following products assigned:
| product |
| Sylius Tee |
| Black T-Shirt |
| Doctrine T-Shirt |
And there are following reviews:
| title | rating | comment | author | product | subject type |
| Lorem ipsum | 5 | Lorem ipsum dolor sit | bar@foo.com | Symfony T-Shirt | product |
Background:
Given there is default currency configured
And there are following taxonomies defined:
| code | name |
| RTX1 | Category |
And taxonomy "Category" has following taxons:
| Clothing[TX1] > T-Shirts[TX2] |
| Clothing[TX1] > PHP T-Shirts[TX3] |
| Clothing[TX1] > Gloves[TX4] |
And there are following channels configured:
| code | name | currencies | locales | url |
| WEB-US | mystore.us | EUR, GBP | en_US | |
| WEB-EU | mystore.eu | USD | en_GB, fr_FR, de_DE | localhost |
And there are following attributes:
| name | type |
| T-Shirt fabric | text |
And the following products exist:
| name | price | taxons | pricing calculator | calculator configuration | attributes |
| Super T-Shirt | 19.99 | T-Shirts | channel_based | WEB-EU:15.99 | T-Shirt fabric:Wool |
| Black T-Shirt | 18.99 | T-Shirts | | | |
| Sylius Tee | 12.99 | PHP T-Shirts | | | |
| Symfony T-Shirt | 15.00 | PHP T-Shirts | | | |
| Doctrine T-Shirt | 15.00 | PHP T-Shirts | | | |
And channel "WEB-EU" has following configuration:
| taxonomy |
| Category |
And channel "WEB-EU" has following products assigned:
| product |
| Super T-Shirt |
| Symfony T-Shirt |
And channel "WEB-US" has following products assigned:
| product |
| Sylius Tee |
| Black T-Shirt |
| Doctrine T-Shirt |
And there are following reviews:
| title | rating | comment | author | product | subject type |
| Lorem ipsum | 5 | Lorem ipsum dolor sit | bar@foo.com | Symfony T-Shirt | product |
Scenario: Browsing products by taxon
Given I am on the store homepage
When I follow "T-Shirts"
Then I should see there 1 products
And I should see "Super T-Shirt"
Scenario: Browsing products by taxon
Given I am on the store homepage
When I follow "T-Shirts"
Then I should see there 1 products
And I should see "Super T-Shirt"
Scenario: Empty index of products
Given there are no products
And I am on the store homepage
When I follow "Gloves"
Then I should see "There are no products to display"
Scenario: Empty index of products
Given there are no products
And I am on the store homepage
When I follow "Gloves"
Then I should see "There are no products to display"
Scenario: Accessing product page via title
Given I am on the store homepage
And I follow "PHP T-Shirts"
When I click "Symfony T-Shirt"
Then I should be on the product page for "Symfony T-Shirt"
Scenario: Accessing product page via title
Given I am on the store homepage
And I follow "PHP T-Shirts"
When I click "Symfony T-Shirt"
Then I should be on the product page for "Symfony T-Shirt"
Scenario: Display only products for current channel
Given I am on the store homepage
Then I should see "Super T-Shirt"
But I should not see "Black T-Shirt"
Scenario: Display only products for current channel
Given I am on the store homepage
Then I should see "Super T-Shirt"
But I should not see "Black T-Shirt"
Scenario: Display proper product price for specific channel
Given I am on the store homepage
Then I should see "Super T-shirt"
And I should see "15.99"
Scenario: Display proper product price for specific channel
Given I am on the store homepage
Then I should see "Super T-shirt"
And I should see "15.99"
Scenario: Display product attributes
Given I am on the product page for "Super T-shirt"
Then I should see "Wool"
Scenario: Display product attributes
Given I am on the product page for "Super T-shirt"
Then I should see "Wool"
Scenario: Receiving exception while entering page for product with empty slug
Given I go to page for product with empty slug
Then the response status code should be 404
Scenario: Receiving exception while entering page for product with empty slug
Given I go to page for product with empty slug
Then the response status code should be 404

View file

@ -0,0 +1,129 @@
@legacy_reviews
Feature: Reviews
In orders to know customer's opinions about my products
As a store owner
I want to be able to manage reviews
Background:
Given there are following users:
| email |
| beth@foo.com |
| martha@foo.com |
| rick@foo.com |
And the following products exist:
| name | price | average rating |
| Black T-Shirt | 18.99 | 4 |
| Sylius Tee | 12.99 | 5 |
| Symfony T-Shirt | 15.00 | 1 |
And there are following reviews:
| title | rating | comment | author | product | subject type | status |
| Really bad | 1 | Lorem ipsum dolor sit | beth@foo.com | Symfony T-Shirt | product | |
| Very good | 4 | Lorem ipsum dolor sit | martha@foo.com | Black T-Shirt | product | |
| Awesome | 5 | Lorem ipsum dolor sit | rick@foo.com | Sylius Tee | product | new |
And there is default currency configured
And there is default channel configured
And I am logged in as administrator
Scenario: Seeing created reviews in the list
Given I am on the dashboard page
When I follow "Product reviews"
Then I should see 3 reviews in the list
Scenario: Adding new review with default options
Given I am on the product review creation page
When I fill in the following:
| Title | New review |
| Comment | Lorem ipsum dolor |
And I select the "5" radio button
And I press "Create"
Then I should be on the page of product review with title "New review"
And I should see "Review has been successfully created."
And I should see "Lorem ipsum dolor"
Scenario: Prevent adding review without required fields
Given I am on the product review creation page
When I press "Create"
Then I should still be on the product review creation page
And I should see "You must check review rating."
And I should see "Review title should not be blank."
Scenario: Accessing review edit page from the list
Given I am on the product review index page
When I press "Edit" near "Very good"
Then I should be editing product review with title "Very good"
Scenario: Accessing review edit page from details page
Given I am on the page of product review with title "Very good"
When I follow "Edit"
Then I should be editing product review with title "Very good"
Scenario: Updating review
Given I am editing product review with title "Very good"
When I fill in "Title" with "Very, very good"
And I press "Save changes"
Then I should be on the page of product review with title "Very, very good"
@javascript
Scenario: Removing review from the list
Given I am on the product review index page
When I press "Delete" near "Awesome"
And I click "Delete" from the confirmation modal
Then I should still be on the product review index page
And I should see "Review has been successfully deleted."
@javascript
Scenario: Removing review from details page
Given I am on the page of product review with title "Awesome"
When I press "Delete"
And I click "Delete" from the confirmation modal
Then I should be on the product review index page
And I should see "Review has been successfully deleted."
Scenario: Showing review details
Given I am on the product review index page
When I click "Details" near "Awesome"
Then I should be on the page of product review with title "Awesome"
And I should see "Awesome"
And I should see "rick@foo.com"
And I should see "Lorem ipsum dolor"
Scenario: Accepting review from the list
Given I am on the product review index page
When I press "Accept" near "Awesome"
Then I should see "Product review has been successfully updated."
When I press "Details" near "Awesome"
Then I should see "accepted"
Scenario: Accepting review from details page
Given I am on the page of product review with title "Awesome"
When I press "Accept"
Then I should see "Product review has been successfully updated."
And I should see "accepted"
And I should not see "Reject" button
And I should not see "Accept" button
Scenario: Rejecting review from the list
Given I am on the product review index page
When I press "Reject" near "Awesome"
And I press "Details" near "Awesome"
Then I should see "rejected"
Scenario: Rejecting review from details page
Given I am on the page of product review with title "Awesome"
When I press "Reject"
Then I should see "Product review has been successfully updated."
And I should see "rejected"
And I should not see "Reject" button
And I should not see "Accept" button
Scenario: Seeing reviews list after customer deletion
Given customer "beth@foo.com" has been deleted
And I am on the product review index page
Then I should see 2 reviews in the list
And I should not see "Really bad"
Scenario: Seeing review list after product deletion
Given product "Symfony T-Shirt" has been deleted
And I am on the product review index page
Then I should see 2 reviews in the list
And I should not see "Really bad"

View file

@ -1,139 +0,0 @@
@reviews
Feature: Reviews
In orders to know customer's opinions about my products
As a store owner
I want to be able to manage reviews
Background:
Given there are following users:
| email |
| beth@foo.com |
| martha@foo.com |
| rick@foo.com |
And there are following taxonomies defined:
| code | name |
| TRX1 | Category |
And taxonomy "Category" has following taxons:
| Clothing[TX1] > T-Shirts[TX2] |
| Clothing[TX1] > PHP T-Shirts[TX3] |
| Clothing[TX1] > PHP T-Gloves[TX4] |
And the following products exist:
| name | price | taxons | average rating |
| Super T-Shirt | 19.99 | T-Shirts | 0 |
| Black T-Shirt | 18.99 | T-Shirts | 4 |
| Sylius Tee | 12.99 | PHP T-Shirts | 5 |
| Symfony T-Shirt | 15.00 | PHP T-Shirts | 1 |
| Doctrine T-Shirt | 15.00 | PHP T-Shirts | 0 |
And there are following reviews:
| title | rating | comment | author | product | subject type | status |
| Really bad | 1 | Lorem ipsum dolor sit | beth@foo.com | Symfony T-Shirt | product | |
| Very good | 4 | Lorem ipsum dolor sit | martha@foo.com | Black T-Shirt | product | |
| Awesome | 5 | Lorem ipsum dolor sit | rick@foo.com | Sylius Tee | product | new |
And there is default currency configured
And there is default channel configured
And I am logged in as administrator
Scenario: Seeing created reviews in the list
Given I am on the dashboard page
When I follow "Product reviews"
Then I should see 3 reviews in the list
Scenario: Adding new review with default options
Given I am on the product review creation page
When I fill in the following:
| Title | New review |
| Comment | Lorem ipsum dolor |
And I select the "5" radio button
And I press "Create"
Then I should be on the page of product review with title "New review"
And I should see "Review has been successfully created."
And I should see "Lorem ipsum dolor"
Scenario: Prevent adding review without required fields
Given I am on the product review creation page
When I press "Create"
Then I should still be on the product review creation page
And I should see "You must check review rating."
And I should see "Review title should not be blank."
Scenario: Accessing review edit page from the list
Given I am on the product review index page
When I press "Edit" near "Very good"
Then I should be editing product review with title "Very good"
Scenario: Accessing review edit page from details page
Given I am on the page of product review with title "Very good"
When I follow "Edit"
Then I should be editing product review with title "Very good"
Scenario: Updating review
Given I am editing product review with title "Very good"
When I fill in "Title" with "Very, very good"
And I press "Save changes"
Then I should be on the page of product review with title "Very, very good"
@javascript
Scenario: Removing review from the list
Given I am on the product review index page
When I press "Delete" near "Awesome"
And I click "Delete" from the confirmation modal
Then I should still be on the product review index page
And I should see "Review has been successfully deleted."
@javascript
Scenario: Removing review from details page
Given I am on the page of product review with title "Awesome"
When I press "Delete"
And I click "Delete" from the confirmation modal
Then I should be on the product review index page
And I should see "Review has been successfully deleted."
Scenario: Showing review details
Given I am on the product review index page
When I click "Details" near "Awesome"
Then I should be on the page of product review with title "Awesome"
And I should see "Awesome"
And I should see "rick@foo.com"
And I should see "Lorem ipsum dolor"
Scenario: Accepting review from the list
Given I am on the product review index page
When I press "Accept" near "Awesome"
Then I should see "Product review has been successfully updated."
When I press "Details" near "Awesome"
Then I should see "accepted"
Scenario: Accepting review from details page
Given I am on the page of product review with title "Awesome"
When I press "Accept"
Then I should see "Product review has been successfully updated."
And I should see "accepted"
And I should not see "Reject" button
And I should not see "Accept" button
Scenario: Rejecting review from the list
Given I am on the product review index page
When I press "Reject" near "Awesome"
Then I should see "Product review has been successfully updated."
When I press "Details" near "Awesome"
Then I should see "rejected"
Scenario: Rejecting review from details page
Given I am on the page of product review with title "Awesome"
When I press "Reject"
Then I should see "Product review has been successfully updated."
And I should see "rejected"
And I should not see "Reject" button
And I should not see "Accept" button
Scenario: Seeing reviews list after customer deletion
Given customer "beth@foo.com" has been deleted
And I am on the product review index page
Then I should see 2 reviews in the list
And I should not see "Really bad"
Scenario: Seeing review list after product deletion
Given product "Symfony T-Shirt" has been deleted
And I am on the product review index page
Then I should see 2 reviews in the list
And I should not see "Really bad"

View file

@ -1,70 +0,0 @@
suites:
Addressing : { namespace: Sylius\Component\Addressing, psr4_prefix: Sylius\Component\Addressing, spec_path: src/Sylius/Component/Addressing, src_path: src/Sylius/Component/Addressing }
Archetype : { namespace: Sylius\Component\Archetype, psr4_prefix: Sylius\Component\Archetype, spec_path: src/Sylius/Component/Archetype, src_path: src/Sylius/Component/Archetype }
Attribute : { namespace: Sylius\Component\Attribute, psr4_prefix: Sylius\Component\Attribute, spec_path: src/Sylius/Component/Attribute, src_path: src/Sylius/Component/Attribute }
Cart : { namespace: Sylius\Component\Cart, psr4_prefix: Sylius\Component\Cart, spec_path: src/Sylius/Component/Cart, src_path: src/Sylius/Component/Cart }
Channel : { namespace: Sylius\Component\Channel, psr4_prefix: Sylius\Component\Channel, spec_path: src/Sylius/Component/Channel, src_path: src/Sylius/Component/Channel }
Contact : { namespace: Sylius\Component\Contact, psr4_prefix: Sylius\Component\Contact, spec_path: src/Sylius/Component/Contact, src_path: src/Sylius/Component/Contact }
Core : { namespace: Sylius\Component\Core, psr4_prefix: Sylius\Component\Core, spec_path: src/Sylius/Component/Core, src_path: src/Sylius/Component/Core }
Currency : { namespace: Sylius\Component\Currency, psr4_prefix: Sylius\Component\Currency, spec_path: src/Sylius/Component/Currency, src_path: src/Sylius/Component/Currency }
Inventory : { namespace: Sylius\Component\Inventory, psr4_prefix: Sylius\Component\Inventory, spec_path: src/Sylius/Component/Inventory, src_path: src/Sylius/Component/Inventory }
Locale : { namespace: Sylius\Component\Locale, psr4_prefix: Sylius\Component\Locale, spec_path: src/Sylius/Component/Locale, src_path: src/Sylius/Component/Locale }
Mailer : { namespace: Sylius\Component\Mailer, psr4_prefix: Sylius\Component\Mailer, spec_path: src/Sylius/Component/Mailer, src_path: src/Sylius/Component/Mailer }
Order : { namespace: Sylius\Component\Order, psr4_prefix: Sylius\Component\Order, spec_path: src/Sylius/Component/Order, src_path: src/Sylius/Component/Order }
Originator : { namespace: Sylius\Component\Originator, psr4_prefix: Sylius\Component\Originator, spec_path: src/Sylius/Component/Originator, src_path: src/Sylius/Component/Originator }
Payment : { namespace: Sylius\Component\Payment, psr4_prefix: Sylius\Component\Payment , spec_path: src/Sylius/Component/Payment, src_path: src/Sylius/Component/Payment }
Pricing : { namespace: Sylius\Component\Pricing, psr4_prefix: Sylius\Component\Pricing , spec_path: src/Sylius/Component/Pricing, src_path: src/Sylius/Component/Pricing }
Product : { namespace: Sylius\Component\Product, psr4_prefix: Sylius\Component\Product , spec_path: src/Sylius/Component/Product, src_path: src/Sylius/Component/Product }
Promotion : { namespace: Sylius\Component\Promotion, psr4_prefix: Sylius\Component\Promotion, spec_path: src/Sylius/Component/Promotion, src_path: src/Sylius/Component/Promotion }
Rbac : { namespace: Sylius\Component\Rbac, psr4_prefix: Sylius\Component\Rbac, spec_path: src/Sylius/Component/Rbac, src_path: src/Sylius/Component/Rbac }
Registry : { namespace: Sylius\Component\Registry, psr4_prefix: Sylius\Component\Registry, spec_path: src/Sylius/Component/Registry, src_path: src/Sylius/Component/Registry }
Report : { namespace: Sylius\Component\Report, psr4_prefix: Sylius\Component\Report , spec_path: src/Sylius/Component/Report, src_path: src/Sylius/Component/Report }
Review : { namespace: Sylius\Component\Review, psr4_prefix: Sylius\Component\Review , spec_path: src/Sylius/Component/Review, src_path: src/Sylius/Component/Review }
Resource : { namespace: Sylius\Component\Resource, psr4_prefix: Sylius\Component\Resource, spec_path: src/Sylius/Component/Resource, src_path: src/Sylius/Component/Resource }
Sequence : { namespace: Sylius\Component\Sequence, psr4_prefix: Sylius\Component\Sequence, spec_path: src/Sylius/Component/Sequence, src_path: src/Sylius/Component/Sequence }
Shipping : { namespace: Sylius\Component\Shipping, psr4_prefix: Sylius\Component\Shipping, spec_path: src/Sylius/Component/Shipping, src_path: src/Sylius/Component/Shipping }
Storage : { namespace: Sylius\Component\Storage, psr4_prefix: Sylius\Component\Storage , spec_path: src/Sylius/Component/Storage, src_path: src/Sylius/Component/Storage }
Taxation : { namespace: Sylius\Component\Taxation, psr4_prefix: Sylius\Component\Taxation, spec_path: src/Sylius/Component/Taxation, src_path: src/Sylius/Component/Taxation }
Taxonomy : { namespace: Sylius\Component\Taxonomy, psr4_prefix: Sylius\Component\Taxonomy, spec_path: src/Sylius/Component/Taxonomy, src_path: src/Sylius/Component/Taxonomy }
Translation : { namespace: Sylius\Component\Translation, psr4_prefix: Sylius\Component\Translation, spec_path: src/Sylius/Component/Translation, src_path: src/Sylius/Component/Translation }
User : { namespace: Sylius\Component\User, psr4_prefix: Sylius\Component\User, spec_path: src/Sylius/Component/User, src_path: src/Sylius/Component/User }
Variation : { namespace: Sylius\Component\Variation, psr4_prefix: Sylius\Component\Variation, spec_path: src/Sylius/Component/Variation, src_path: src/Sylius/Component/Variation }
AddressingBundle : { namespace: Sylius\Bundle\AddressingBundle, psr4_prefix: Sylius\Bundle\AddressingBundle, spec_path: src/Sylius/Bundle/AddressingBundle, src_path: src/Sylius/Bundle/AddressingBundle }
ApiBundle : { namespace: Sylius\Bundle\ApiBundle, psr4_prefix: Sylius\Bundle\ApiBundle, spec_path: src/Sylius/Bundle/ApiBundle, src_path: src/Sylius/Bundle/ApiBundle }
ArchetypeBundle : { namespace: Sylius\Bundle\ArchetypeBundle, psr4_prefix: Sylius\Bundle\ArchetypeBundle, spec_path: src/Sylius/Bundle/ArchetypeBundle, src_path: src/Sylius/Bundle/ArchetypeBundle }
AttributeBundle : { namespace: Sylius\Bundle\AttributeBundle, psr4_prefix: Sylius\Bundle\AttributeBundle, spec_path: src/Sylius/Bundle/AttributeBundle, src_path: src/Sylius/Bundle/AttributeBundle }
CartBundle : { namespace: Sylius\Bundle\CartBundle, psr4_prefix: Sylius\Bundle\CartBundle, spec_path: src/Sylius/Bundle/CartBundle, src_path: src/Sylius/Bundle/CartBundle }
ChannelBundle : { namespace: Sylius\Bundle\ChannelBundle, psr4_prefix: Sylius\Bundle\ChannelBundle, spec_path: src/Sylius/Bundle/ChannelBundle, src_path: src/Sylius/Bundle/ChannelBundle }
ContactBundle : { namespace: Sylius\Bundle\ContactBundle, psr4_prefix: Sylius\Bundle\ContactBundle, spec_path: src/Sylius/Bundle/ContactBundle, src_path: src/Sylius/Bundle/ContactBundle }
ContentBundle : { namespace: Sylius\Bundle\ContentBundle, psr4_prefix: Sylius\Bundle\ContentBundle, spec_path: src/Sylius/Bundle/ContentBundle, src_path: src/Sylius/Bundle/ContentBundle }
CoreBundle : { namespace: Sylius\Bundle\CoreBundle, psr4_prefix: Sylius\Bundle\CoreBundle, spec_path: src/Sylius/Bundle/CoreBundle, src_path: src/Sylius/Bundle/CoreBundle }
CurrencyBundle : { namespace: Sylius\Bundle\CurrencyBundle, psr4_prefix: Sylius\Bundle\CurrencyBundle , spec_path: src/Sylius/Bundle/CurrencyBundle, src_path: src/Sylius/Bundle/CurrencyBundle }
FlowBundle : { namespace: Sylius\Bundle\FlowBundle, psr4_prefix: Sylius\Bundle\FlowBundle, spec_path: src/Sylius/Bundle/FlowBundle, src_path: src/Sylius/Bundle/FlowBundle }
InstallerBundle : { namespace: Sylius\Bundle\InstallerBundle, psr4_prefix: Sylius\Bundle\InstallerBundle, spec_path: src/Sylius/Bundle/InstallerBundle, src_path: src/Sylius/Bundle/InstallerBundle }
InventoryBundle : { namespace: Sylius\Bundle\InventoryBundle, psr4_prefix: Sylius\Bundle\InventoryBundle, spec_path: src/Sylius/Bundle/InventoryBundle, src_path: src/Sylius/Bundle/InventoryBundle }
LocaleBundle : { namespace: Sylius\Bundle\LocaleBundle, psr4_prefix: Sylius\Bundle\LocaleBundle, spec_path: src/Sylius/Bundle/LocaleBundle, src_path: src/Sylius/Bundle/LocaleBundle }
MailerBundle : { namespace: Sylius\Bundle\MailerBundle, psr4_prefix: Sylius\Bundle\MailerBundle, spec_path: src/Sylius/Bundle/MailerBundle, src_path: src/Sylius/Bundle/MailerBundle }
MoneyBundle : { namespace: Sylius\Bundle\MoneyBundle, psr4_prefix: Sylius\Bundle\MoneyBundle, spec_path: src/Sylius/Bundle/MoneyBundle, src_path: src/Sylius/Bundle/MoneyBundle }
OrderBundle : { namespace: Sylius\Bundle\OrderBundle, psr4_prefix: Sylius\Bundle\OrderBundle, spec_path: src/Sylius/Bundle/OrderBundle, src_path: src/Sylius/Bundle/OrderBundle }
PaymentBundle : { namespace: Sylius\Bundle\PaymentBundle, psr4_prefix: Sylius\Bundle\PaymentBundle, spec_path: src/Sylius/Bundle/PaymentBundle, src_path: src/Sylius/Bundle/PaymentBundle }
PayumBundle : { namespace: Sylius\Bundle\PayumBundle, psr4_prefix: Sylius\Bundle\PayumBundle, spec_path: src/Sylius/Bundle/PayumBundle, src_path: src/Sylius/Bundle/PayumBundle }
PricingBundle : { namespace: Sylius\Bundle\PricingBundle, psr4_prefix: Sylius\Bundle\PricingBundle, spec_path: src/Sylius/Bundle/PricingBundle, src_path: src/Sylius/Bundle/PricingBundle }
ProductBundle : { namespace: Sylius\Bundle\ProductBundle, psr4_prefix: Sylius\Bundle\ProductBundle, spec_path: src/Sylius/Bundle/ProductBundle, src_path: src/Sylius/Bundle/ProductBundle }
PromotionBundle : { namespace: Sylius\Bundle\PromotionBundle, psr4_prefix: Sylius\Bundle\PromotionBundle, spec_path: src/Sylius/Bundle/PromotionBundle, src_path: src/Sylius/Bundle/PromotionBundle }
RbacBundle : { namespace: Sylius\Bundle\RbacBundle, psr4_prefix: Sylius\Bundle\RbacBundle, spec_path: src/Sylius/Bundle/RbacBundle, src_path: src/Sylius/Bundle/RbacBundle }
ReportBundle : { namespace: Sylius\Bundle\ReportBundle, psr4_prefix: Sylius\Bundle\ReportBundle , spec_path: src/Sylius/Bundle/ReportBundle, src_path: src/Sylius/Bundle/ReportBundle }
ResourceBundle : { namespace: Sylius\Bundle\ResourceBundle, psr4_prefix: Sylius\Bundle\ResourceBundle , spec_path: src/Sylius/Bundle/ResourceBundle, src_path: src/Sylius/Bundle/ResourceBundle }
ReviewBundle : { namespace: Sylius\Bundle\ReviewBundle, psr4_prefix: Sylius\Bundle\ReviewBundle , spec_path: src/Sylius/Bundle/ReviewBundle, src_path: src/Sylius/Bundle/ReviewBundle }
SearchBundle : { namespace: Sylius\Bundle\SearchBundle, psr4_prefix: Sylius\Bundle\SearchBundle, spec_path: src/Sylius/Bundle/SearchBundle, src_path: src/Sylius/Bundle/SearchBundle }
SequenceBundle : { namespace: Sylius\Bundle\SequenceBundle, psr4_prefix: Sylius\Bundle\SequenceBundle , spec_path: src/Sylius/Bundle/SequenceBundle, src_path: src/Sylius/Bundle/SequenceBundle }
SettingsBundle : { namespace: Sylius\Bundle\SettingsBundle, psr4_prefix: Sylius\Bundle\SettingsBundle , spec_path: src/Sylius/Bundle/SettingsBundle, src_path: src/Sylius/Bundle/SettingsBundle }
ShippingBundle : { namespace: Sylius\Bundle\ShippingBundle, psr4_prefix: Sylius\Bundle\ShippingBundle , spec_path: src/Sylius/Bundle/ShippingBundle, src_path: src/Sylius/Bundle/ShippingBundle }
TaxationBundle : { namespace: Sylius\Bundle\TaxationBundle, psr4_prefix: Sylius\Bundle\TaxationBundle , spec_path: src/Sylius/Bundle/TaxationBundle, src_path: src/Sylius/Bundle/TaxationBundle }
TaxonomyBundle : { namespace: Sylius\Bundle\TaxonomyBundle, psr4_prefix: Sylius\Bundle\TaxonomyBundle , spec_path: src/Sylius/Bundle/TaxonomyBundle, src_path: src/Sylius/Bundle/TaxonomyBundle }
TranslationBundle : { namespace: Sylius\Bundle\TranslationBundle, psr4_prefix: Sylius\Bundle\TranslationBundle , spec_path: src/Sylius/Bundle/TranslationBundle, src_path: src/Sylius/Bundle/TranslationBundle }
VariationBundle : { namespace: Sylius\Bundle\VariationBundle, psr4_prefix: Sylius\Bundle\VariationBundle, spec_path: src/Sylius/Bundle/VariationBundle, src_path: src/Sylius/Bundle/VariationBundle }
UserBundle : { namespace: Sylius\Bundle\UserBundle, psr4_prefix: Sylius\Bundle\UserBundle, spec_path: src/Sylius/Bundle/UserBundle, src_path: src/Sylius/Bundle/UserBundle }
extensions:
- Akeneo\SkipExampleExtension

View file

@ -21,6 +21,7 @@ suites:
Registry: { namespace: Sylius\Component\Registry, psr4_prefix: Sylius\Component\Registry, spec_path: src/Sylius/Component/Registry, src_path: src/Sylius/Component/Registry }
Report: { namespace: Sylius\Component\Report, psr4_prefix: Sylius\Component\Report, spec_path: src/Sylius/Component/Report, src_path: src/Sylius/Component/Report }
Resource: { namespace: Sylius\Component\Resource, psr4_prefix: Sylius\Component\Resource, spec_path: src/Sylius/Component/Resource, src_path: src/Sylius/Component/Resource }
Review: { namespace: Sylius\Component\Review, psr4_prefix: Sylius\Component\Review, spec_path: src/Sylius/Component/Review, src_path: src/Sylius/Component/Review }
Sequence: { namespace: Sylius\Component\Sequence, psr4_prefix: Sylius\Component\Sequence, spec_path: src/Sylius/Component/Sequence, src_path: src/Sylius/Component/Sequence }
Shipping: { namespace: Sylius\Component\Shipping, psr4_prefix: Sylius\Component\Shipping, spec_path: src/Sylius/Component/Shipping, src_path: src/Sylius/Component/Shipping }
Storage: { namespace: Sylius\Component\Storage, psr4_prefix: Sylius\Component\Storage, spec_path: src/Sylius/Component/Storage, src_path: src/Sylius/Component/Storage }
@ -57,6 +58,7 @@ suites:
RbacBundle: { namespace: Sylius\Bundle\RbacBundle, psr4_prefix: Sylius\Bundle\RbacBundle, spec_path: src/Sylius/Bundle/RbacBundle, src_path: src/Sylius/Bundle/RbacBundle }
ReportBundle: { namespace: Sylius\Bundle\ReportBundle, psr4_prefix: Sylius\Bundle\ReportBundle, spec_path: src/Sylius/Bundle/ReportBundle, src_path: src/Sylius/Bundle/ReportBundle }
ResourceBundle: { namespace: Sylius\Bundle\ResourceBundle, psr4_prefix: Sylius\Bundle\ResourceBundle, spec_path: src/Sylius/Bundle/ResourceBundle, src_path: src/Sylius/Bundle/ResourceBundle }
ReviewBundle: { namespace: Sylius\Bundle\ReviewBundle, psr4_prefix: Sylius\Bundle\ReviewBundle, spec_path: src/Sylius/Bundle/ReviewBundle, src_path: src/Sylius/Bundle/ReviewBundle }
SearchBundle: { namespace: Sylius\Bundle\SearchBundle, psr4_prefix: Sylius\Bundle\SearchBundle, spec_path: src/Sylius/Bundle/SearchBundle, src_path: src/Sylius/Bundle/SearchBundle }
SequenceBundle: { namespace: Sylius\Bundle\SequenceBundle, psr4_prefix: Sylius\Bundle\SequenceBundle, spec_path: src/Sylius/Bundle/SequenceBundle, src_path: src/Sylius/Bundle/SequenceBundle }
SettingsBundle: { namespace: Sylius\Bundle\SettingsBundle, psr4_prefix: Sylius\Bundle\SettingsBundle, spec_path: src/Sylius/Bundle/SettingsBundle, src_path: src/Sylius/Bundle/SettingsBundle }

View file

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\CoreBundle\EventListener;
use Sylius\Bundle\ReviewBundle\Remover\ReviewerReviewsRemoverInterface;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Sylius\Component\Review\Model\ReviewerInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class CustomerReviewsDeleteListener
{
/**
* @var ReviewerReviewsRemoverInterface
*/
private $reviewerReviewsRemover;
/**
* @param ReviewerReviewsRemoverInterface $reviewerReviewsRemover
*/
public function __construct(ReviewerReviewsRemoverInterface $reviewerReviewsRemover)
{
$this->reviewerReviewsRemover = $reviewerReviewsRemover;
}
/**
* @param GenericEvent $event
*/
public function removeCustomerReviews(GenericEvent $event)
{
$author = $event->getSubject();
if (!$author instanceof ReviewerInterface) {
throw new UnexpectedTypeException($author, ReviewerInterface::class);
}
$this->reviewerReviewsRemover->removeReviewerReviews($author);
}
}

View file

@ -43,7 +43,7 @@ class ProductDeleteListener
return;
}
$reviewManager = $args->getEntityManager();
$reviewManager = $this->container->get('sylius.manager.product_review');
$reviewRepository = $this->container->get('sylius.repository.product_review');
$reviews = $reviewRepository->findBy(['reviewSubject' => $product]);
@ -51,7 +51,7 @@ class ProductDeleteListener
$reviewManager->remove($review);
}
$product->setAverageRating(null);
$product->setAverageRating(0);
$reviewManager->flush();
}

View file

@ -11,8 +11,10 @@
namespace Sylius\Bundle\CoreBundle\Form\Type;
use Sylius\Component\Core\Model\Customer;
use Sylius\Component\Core\Model\Product;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @author Daniel Richter <nexyz9@gmail.com>
@ -30,12 +32,12 @@ class ProductReviewAdminType extends ProductReviewType
$builder
->add('reviewSubject', 'entity', [
'class' => 'Sylius\Component\Core\Model\Product',
'class' => Product::class,
'label' => 'sylius.form.review.product',
'property' => 'name',
])
->add('author', 'entity', [
'class' => 'Sylius\Component\Core\Model\Customer',
'class' => Customer::class,
'label' => 'sylius.form.review.author',
'property' => 'email',
])
@ -45,7 +47,7 @@ class ProductReviewAdminType extends ProductReviewType
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'rating_steps' => 5,
@ -59,6 +61,6 @@ class ProductReviewAdminType extends ProductReviewType
*/
public function getName()
{
return sprintf('sylius_%s_review_admin', $this->subject);
return 'sylius_product_review_admin';
}
}

View file

@ -16,6 +16,6 @@ use Sylius\Bundle\ReviewBundle\Form\Type\ReviewType;
/**
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
*/
class ProductReviewType extends ReviewType
class ProductReviewType extends ReviewType
{
}

View file

@ -820,4 +820,4 @@ sylius_review:
admin: Sylius\Bundle\CoreBundle\Form\Type\ProductReviewAdminType
reviewer:
classes:
model: Sylius\Component\Core\Model\Customer
model: %sylius.model.customer.class%

View file

@ -20,7 +20,7 @@
<mapped-superclass name="Sylius\Component\Core\Model\Product" table="sylius_product">
<field name="variantSelectionMethod" column="variant_selection_method" type="string" nullable="false" />
<field name="averageRating" column="average_rating" type="float" nullable="true" />
<field name="averageRating" column="average_rating" type="float" />
<many-to-one field="shippingCategory" target-entity="Sylius\Component\Shipping\Model\ShippingCategoryInterface">
<join-column name="shipping_category_id" referenced-column-name="id" nullable="true" on-delete="SET NULL" />
@ -56,6 +56,7 @@
<many-to-one field="mainTaxon" target-entity="Sylius\Component\Taxonomy\Model\TaxonInterface">
<join-column name="main_taxon_id" referenced-column-name="id" nullable="true" />
</many-to-one>
<gedmo:loggable />
</mapped-superclass>

View file

@ -13,7 +13,6 @@
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping">
<mapped-superclass name="Sylius\Component\Core\Model\ProductReview" table="sylius_product_review">
</mapped-superclass>
<mapped-superclass name="Sylius\Component\Core\Model\ProductReview" table="sylius_product_review" />
</doctrine-mapping>

View file

@ -48,8 +48,6 @@
<parameter key="sylius.form.type.promotion_rule.customer_group_configuration.class">Sylius\Bundle\CoreBundle\Form\Type\Rule\CustomerGroupType</parameter>
<parameter key="sylius.form.type.promotion_action.shipping_discount_configuration.class">Sylius\Bundle\CoreBundle\Form\Type\Action\ShippingDiscountConfigurationType</parameter>
<parameter key="sylius.form.type.promotion_action.add_product_configuration.class">Sylius\Bundle\CoreBundle\Form\Type\Action\AddProductConfigurationType</parameter>
<parameter key="sylius.form.type.product_review_admin.class">Sylius\Bundle\CoreBundle\Form\Type\ProductReviewAdminType</parameter>
</parameters>
<services>

View file

@ -61,7 +61,7 @@
<parameter key="sylius.listener.channel.class">Sylius\Bundle\CoreBundle\EventListener\ChannelDeletionListener</parameter>
<parameter key="sylius.listener.customer_aware.class">Sylius\Bundle\CoreBundle\EventListener\CustomerAwareListener</parameter>
<parameter key="sylius.listener.product_delete.class">Sylius\Bundle\CoreBundle\EventListener\ProductDeleteListener</parameter>
<parameter key="sylius.listener.customer_delete.class">Sylius\Bundle\CoreBundle\EventListener\CustomerDeleteListener</parameter>
<parameter key="sylius.listener.customer_reviews_delete.class">Sylius\Bundle\CoreBundle\EventListener\CustomerReviewsDeleteListener</parameter>
<parameter key="sylius.active_promotions_by_channel_provider.class">Sylius\Component\Core\Provider\ActivePromotionsByChannelProvider</parameter>
@ -394,10 +394,8 @@
<tag name="kernel.event_listener" event="sylius.taxonomy.pre_create" method="uploadTaxonomyImage" />
<tag name="kernel.event_listener" event="sylius.taxonomy.pre_update" method="uploadTaxonomyImage" />
</service>
<service id="sylius.listener.customer_delete" class="%sylius.listener.customer_delete.class%">
<argument type="service" id="sylius.repository.product_review" />
<argument type="service" id="sylius.manager.product_review" />
<argument type="service" id="sylius.review.calculator.average_rating" />
<service id="sylius.listener.customer_reviews_delete" class="%sylius.listener.customer_reviews_delete.class%">
<argument type="service" id="sylius.review.remover.reviewer_reviews" />
<tag name="kernel.event_listener" event="sylius.customer.pre_delete" method="removeCustomerReviews" />
</service>
<service id="sylius.listener.product_delete" class="%sylius.listener.product_delete.class%">

View file

@ -199,7 +199,7 @@ winzou_state_machine:
to: rejected
callbacks:
after:
update_review_subject_average_price:
update_price:
on: 'accept'
do: [@sylius.review.updater.reviewable_average_rating, updateFromReview]
do: [@sylius.review.updater.average_rating, updateFromReview]
args: ['object']

View file

@ -1,69 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\Sylius\Bundle\CoreBundle\EventListener;
use Doctrine\Common\Persistence\ObjectManager;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Sylius\Component\Review\Calculator\AverageRatingCalculatorInterface;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
class CustomerDeleteListenerSpec extends ObjectBehavior
{
function let(EntityRepository $reviewRepository, ObjectManager $reviewManager, AverageRatingCalculatorInterface $averageRatingCalculator)
{
$this->beConstructedWith($reviewRepository, $reviewManager, $averageRatingCalculator);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\CoreBundle\EventListener\CustomerDeleteListener');
}
function it_removes_soft_deleted_customer_reviews_and_recalculates_their_product_ratings(
$averageRatingCalculator,
$reviewRepository,
$reviewManager,
GenericEvent $event,
CustomerInterface $author,
ReviewableInterface $reviewSubject,
ReviewInterface $review
) {
$event->getSubject()->willReturn($author)->shouldBeCalled();
$reviewRepository->findBy(['author' => $author])->willReturn([$review])->shouldBeCalled();
$review->getReviewSubject()->willReturn($reviewSubject)->shouldBeCalled();
$reviewManager->remove($review)->shouldBeCalled();
$reviewManager->flush()->shouldBeCalled();
$averageRatingCalculator->calculate($reviewSubject)->willReturn(0)->shouldBeCalled();
$reviewSubject->setAverageRating(0)->shouldBeCalled();
$this->removeCustomerReviews($event);
}
function it_throws_exception_if_event_subject_is_not_customer_object(GenericEvent $event)
{
$event->getSubject()->willReturn('badObject')->shouldBeCalled();
$this->shouldThrow(new UnexpectedTypeException('badObject', 'Sylius\Component\Core\Model\CustomerInterface'))->during('removeCustomerReviews', [$event]);
}
}

View file

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\Sylius\Bundle\CoreBundle\EventListener;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ReviewBundle\Remover\ReviewerReviewsRemover;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Sylius\Component\Review\Model\ReviewerInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class CustomerReviewsDeleteListenerSpec extends ObjectBehavior
{
function let(ReviewerReviewsRemover $reviewerReviewsRemover)
{
$this->beConstructedWith($reviewerReviewsRemover);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\CoreBundle\EventListener\CustomerReviewsDeleteListener');
}
function it_removes_soft_deleted_customer_reviews_and_recalculates_their_product_ratings(
$reviewerReviewsRemover,
GenericEvent $event,
ReviewerInterface $author
) {
$event->getSubject()->willReturn($author);
$reviewerReviewsRemover->removeReviewerReviews($author)->shouldBeCalled();
$this->removeCustomerReviews($event);
}
function it_throws_exception_if_event_subject_is_not_customer_object(GenericEvent $event)
{
$event->getSubject()->willReturn('badObject')->shouldBeCalled();
$this->shouldThrow(new UnexpectedTypeException('badObject', ReviewerInterface::class))->during('removeCustomerReviews', [$event]);
}
}

View file

@ -45,7 +45,7 @@ class ProductDeleteListenerSpec extends ObjectBehavior
) {
$args->getEntity()->willReturn($product)->shouldBeCalled();
$args->getEntityManager()->willReturn($reviewManager)->shouldBeCalled();
$container->get('sylius.manager.product_review')->willReturn($reviewManager)->shouldBeCalled();
$container->get('sylius.repository.product_review')->willReturn($reviewRepository)->shouldBeCalled();
$reviewRepository->findBy(['reviewSubject' => $product])->willReturn([$review])->shouldBeCalled();
@ -53,7 +53,7 @@ class ProductDeleteListenerSpec extends ObjectBehavior
$reviewManager->remove($review)->shouldBeCalled();
$reviewManager->flush()->shouldBeCalled();
$product->setAverageRating(null)->shouldBeCalled();
$product->setAverageRating(0)->shouldBeCalled();
$this->postSoftDelete($args);
}

View file

@ -12,10 +12,13 @@
namespace spec\Sylius\Bundle\CoreBundle\Form\Type;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\CoreBundle\Form\Type\ProductReviewType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ProductReviewAdminTypeSpec extends ObjectBehavior
{
@ -31,62 +34,43 @@ class ProductReviewAdminTypeSpec extends ObjectBehavior
function it_extends_product_review_admin_type()
{
$this->shouldHaveType('Sylius\Bundle\CoreBundle\Form\Type\ProductReviewType');
$this->shouldHaveType(ProductReviewType::class);
}
function it_builds_form(FormBuilderInterface $builder)
{
$builder
->add('rating', 'choice', [
'choices' => [1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5],
'label' => 'sylius.form.review.rating',
'expanded' => true,
'multiple' => false,
])
->add('rating', 'choice', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('author', 'sylius_customer_guest', [
'label' => false,
])
->add('author', 'sylius_customer_guest', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('title', 'text', [
'label' => 'sylius.form.review.title',
])
->add('title', 'text', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('comment', 'textarea', [
'label' => 'sylius.form.review.comment',
])
->add('comment', 'textarea', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('author', 'entity', [
'class' => 'Sylius\Component\Core\Model\Customer',
'label' => 'sylius.form.review.author',
'property' => 'email',
])
->add('author', 'entity', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('reviewSubject', 'entity', [
'class' => 'Sylius\Component\Core\Model\Product',
'label' => 'sylius.form.review.product',
'property' => 'name',
])
->add('reviewSubject', 'entity', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;

View file

@ -12,6 +12,7 @@
namespace spec\Sylius\Bundle\CoreBundle\Form\Type;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ReviewBundle\Form\Type\ReviewType;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
@ -30,7 +31,7 @@ class ProductReviewTypeSpec extends ObjectBehavior
function it_extends_review_type()
{
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Form\Type\ReviewType');
$this->shouldHaveType(ReviewType::class);
}
function it_has_name()

View file

@ -94,10 +94,6 @@ class ProductContext extends DefaultContext
$this->configureProductPricingCalculator($product, $data);
}
if (isset($data['average rating'])) {
$product->setAverageRating($data['average rating']);
}
$manager->persist($product);
}
@ -383,6 +379,7 @@ class ProductContext extends DefaultContext
$manager->persist($product);
$manager->flush();
}
/**
* @Given /^product "([^"]*)" has been deleted$/
*/
@ -391,7 +388,7 @@ class ProductContext extends DefaultContext
$this->getSession()->visit($this->generatePageUrl('sylius_backend_product_index'));
$tr = $this->assertSession()->elementExists('css', sprintf('table tbody tr:contains("%s")', $productName));
$locator = sprintf('button:contains("%s")', 'Delete');
$locator = 'button:contains("Delete")';
$tr->find('css', $locator)->press();
}
}

View file

@ -22,27 +22,9 @@
<parameter key="sylius.generator.product_variant.class">Sylius\Bundle\ProductBundle\Generator\VariantGenerator</parameter>
<parameter key="sylius.validator.product.unique.class">Sylius\Bundle\ProductBundle\Validator\ProductUniqueValidator</parameter>
<parameter key="sylius.form.type.product_translation.class">Sylius\Bundle\ProductBundle\Form\Type\ProductTranslationType</parameter>
</parameters>
<services>
<!--Forms-->
<service id="sylius.form.type.product" class="%sylius.form.type.product.class%">
<argument>%sylius.model.product.class%</argument>
<argument>%sylius.validation_groups.product%</argument>
<tag name="form.type" alias="sylius_product" />
</service>
<service id="sylius.form.type.product_archetype" class="%sylius.form.type.product_archetype.class%">
<argument>%sylius.model.product_archetype.class%</argument>
<argument>%sylius.validation_groups.product_archetype%</argument>
<tag name="form.type" alias="sylius_product_archetype" />
</service>
<service id="sylius.form.type.product_translation" class="%sylius.form.type.product_translation.class%">
<argument>%sylius.model.product.class%</argument>
<argument>%sylius.validation_groups.product%</argument>
<tag name="form.type" alias="sylius_product_translation" />
</service>
<service id="sylius.set_builder.cartesian" class="%sylius.set_builder.cartesian.class%" />
<service id="sylius.generator.product_variant" class="%sylius.generator.product_variant.class%">
<argument type="service" id="sylius.factory.product_variant" />

View file

@ -102,11 +102,6 @@ class ResourceController extends Controller
*/
protected $eventDispatcher;
/**
* @var string
*/
protected $stateMachineGraph;
/**
* @param MetadataInterface $metadata
* @param RequestConfigurationFactoryInterface $requestConfigurationFactory
@ -466,15 +461,11 @@ class ResourceController extends Controller
*
* @return RedirectResponse
*/
public function updateStateAction(Request $request, $transition, $graph = null)
public function updateStateAction(Request $request, $transition, $graph)
{
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
$resource = $this->findOr404($configuration);
if (null === $graph) {
$graph = $this->stateMachineGraph;
}
$stateMachine = $this->get('sm.factory')->get($resource, $graph);
if (!$stateMachine->can($transition)) {
throw new NotFoundHttpException(sprintf(

View file

@ -1,14 +1,18 @@
language: php
sudo: false
php:
- 5.3
- 5.4
- 7.0
- 5.6
- 5.5
before_script: composer install --prefer-source --no-interaction
before_install:
- echo "memory_limit=2048M" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- phpenv config-rm xdebug.ini || true
script: bin/phpspec run -fpretty --verbose
install:
- composer install --no-interaction --prefer-source
notifications:
email: "travis-ci@sylius.org"
irc: "irc.freenode.org#sylius-dev"
script:
- bin/phpspec run -fpretty --verbose

View file

@ -61,6 +61,11 @@ class ReviewContext extends DefaultContext
$review->setStatus((isset($reviewHash['status']) && '' !== $reviewHash['status']) ? $reviewHash['status'] : ReviewInterface::STATUS_ACCEPTED);
if (ReviewInterface::STATUS_ACCEPTED === $review->getStatus()) {
$averageRatingCalculator = $this->getService('sylius.review.calculator.average_rating');
$product->setAverageRating($averageRatingCalculator->calculate($product));
}
return $review;
}

View file

@ -1,6 +1,6 @@
CHANGELOG
=========
### v0.16.0
### v0.17.0
* Initial dev release.
* Initial dev release.

View file

@ -1,26 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ReviewBundle\Controller;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
class ReviewController extends ResourceController
{
/**
* @var string
*/
protected $stateMachineGraph = ReviewInterface::REVIEW_STATE_MACHINE_GRAPH;
}

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ReviewBundle\DependencyInjection\Compiler;
use Sylius\Component\Review\Factory\ReviewFactory;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
/**
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class RegisterReviewFactoryPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
foreach ($container->getParameter('sylius.review.subjects') as $subject => $configuration) {
$factory = $container->findDefinition('sylius.factory.'.$subject.'_review');
$reviewFactoryDefinition = new Definition(ReviewFactory::class);
$reviewFactory = $container->setDefinition(sprintf('sylius.factory.'.$subject.'_review'), $reviewFactoryDefinition);
$reviewFactory->addArgument($factory);
$reviewFactory->addArgument($container->findDefinition('sylius.repository.'.$subject));
}
}
}

View file

@ -11,7 +11,13 @@
namespace Sylius\Bundle\ReviewBundle\DependencyInjection;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Sylius\Bundle\ReviewBundle\Doctrine\ORM\ReviewRepository;
use Sylius\Bundle\ReviewBundle\Form\Type\ReviewType;
use Sylius\Component\Resource\Factory\Factory;
use Sylius\Component\Review\Model\ReviewerInterface;
use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
@ -38,7 +44,7 @@ class Configuration implements ConfigurationInterface
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('driver')->isRequired()->cannotBeEmpty()->end()
->scalarNode('driver')->defaultValue(SyliusResourceBundle::DRIVER_DOCTRINE_ORM)->end()
->end()
;
@ -67,14 +73,14 @@ class Configuration implements ConfigurationInterface
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->isRequired()->end()
->scalarNode('interface')->defaultValue('Sylius\Component\Review\Model\ReviewInterface')->end()
->scalarNode('controller')->defaultValue('Sylius\Bundle\ResourceBundle\Controller\ResourceController')->end()
->scalarNode('repository')->cannotBeEmpty()->end()
->scalarNode('factory')->defaultValue(Factory::class)->end()
->scalarNode('interface')->defaultValue(ReviewInterface::class)->cannotBeEmpty()->end()
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
->scalarNode('repository')->defaultValue(ReviewRepository::class)->cannotBeEmpty()->end()
->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end()
->arrayNode('form')
->addDefaultsIfNotSet()
->children()
->scalarNode('default')->defaultValue('Sylius\Bundle\ReviewBundle\Form\Type\ReviewType')->cannotBeEmpty()->end()
->scalarNode('default')->defaultValue(ReviewType::class)->cannotBeEmpty()->end()
->scalarNode('admin')->isRequired()->end()
->end()
->end()
@ -102,7 +108,9 @@ class Configuration implements ConfigurationInterface
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->isRequired()->end()
->scalarNode('factory')->defaultValue(Factory::class)->end()
->scalarNode('interface')->defaultValue(ReviewerInterface::class)->cannotBeEmpty()->end()
->scalarNode('repository')->cannotBeEmpty()->end()
->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end()
->end()
->end()
->arrayNode('validation_groups')

View file

@ -82,8 +82,8 @@ class SyliusReviewExtension extends AbstractResourceExtension
}
/**
* @param string $name
* @param array $parameters
* @param string $name
* @param array $parameters
* @param ContainerBuilder $container
*/
private function addRequiredArgumentsToForms($name, array $parameters, ContainerBuilder $container)
@ -100,7 +100,7 @@ class SyliusReviewExtension extends AbstractResourceExtension
}
/**
* @param string $resourceName
* @param string $resourceName
* @param ContainerBuilder $container
*/
private function addProperTagToReviewDeleteListener($resourceName, ContainerBuilder $container)
@ -110,6 +110,6 @@ class SyliusReviewExtension extends AbstractResourceExtension
}
$listenerDefinition = $container->getDefinition('sylius.listener.review_delete');
$listenerDefinition->addTag('kernel.event_listener', ['event' => 'sylius.'.$resourceName.'_review.post_delete', 'method' => 'recalculateSubjectRating']);
$listenerDefinition->addTag('doctrine.event_listener', ['event' => 'postRemove', 'method' => 'recalculateSubjectRating']);
}
}

View file

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ReviewBundle\Doctrine\ORM;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ReviewRepository extends EntityRepository
{
/**
* @param int $reviewSubjectId
*
* @return ReviewInterface[]
*/
public function findAcceptedBySubjectId($reviewSubjectId)
{
return $this
->getCollectionQueryBuilder()
->andWhere('o.reviewSubject = :reviewSubject')
->andWhere('o.status = :status')
->setParameter('reviewSubject', $reviewSubjectId)
->setParameter('status', ReviewInterface::STATUS_ACCEPTED)
->getQuery()
->getResult()
;
}
}

View file

@ -12,6 +12,7 @@
namespace Sylius\Bundle\ReviewBundle\EventListener;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
/**
@ -38,7 +39,9 @@ class LoadMetadataSubscriber implements EventSubscriber
*/
public function getSubscribedEvents()
{
return ['loadClassMetadata'];
return [
'loadClassMetadata',
];
}
/**
@ -56,48 +59,75 @@ class LoadMetadataSubscriber implements EventSubscriber
$reviewableEntityMetadata = $metadataFactory->getMetadataFor($reviewableEntity);
$reviewerEntityMetadata = $metadataFactory->getMetadataFor($reviewerEntity);
$subjectMapping = [
'fieldName' => 'reviewSubject',
'targetEntity' => $reviewableEntity,
'inversedBy' => 'reviews',
'joinColumns' => [
[
'name' => $subject.'_id',
'referencedColumnName' => $reviewableEntityMetadata->fieldMappings['id']['columnName'],
'nullable' => false,
'onDelete' => 'CASCADE',
],
],
];
$reviewerMapping = [
'fieldName' => 'author',
'targetEntity' => $reviewerEntity,
'joinColumn' => [
'name' => 'customer_id',
'referencedColumnName' => $reviewerEntityMetadata->fieldMappings['id']['columnName'],
'nullable' => false,
'onDelete' => 'CASCADE',
],
'cascade' => ['persist'],
];
$metadata->mapManyToOne($subjectMapping);
$metadata->mapManyToOne($reviewerMapping);
$metadata->mapManyToOne($this->createSubjectMapping($reviewableEntity, $subject, $reviewableEntityMetadata));
$metadata->mapManyToOne($this->createReviewerMapping($reviewerEntity, $reviewerEntityMetadata));
}
if ($class['subject'] === $metadata->getName()) {
$reviewEntity = $class['review']['classes']['model'];
$reviewsMapping = [
'fieldName' => 'reviews',
'targetEntity' => $reviewEntity,
'mappedBy' => 'reviewSubject',
'cascade' => ['all'],
];
$metadata->mapOneToMany($reviewsMapping);
$metadata->mapOneToMany($this->createReviewsMapping($reviewEntity));
}
}
}
/**
* @param string $reviewableEntity
* @param string $subject
* @param ClassMetadata $reviewableEntityMetadata
*
* @return array
*/
private function createSubjectMapping($reviewableEntity, $subject, ClassMetadata $reviewableEntityMetadata)
{
return [
'fieldName' => 'reviewSubject',
'targetEntity' => $reviewableEntity,
'inversedBy' => 'reviews',
'joinColumns' => [
[
'name' => $subject.'_id',
'referencedColumnName' => $reviewableEntityMetadata->fieldMappings['id']['columnName'],
'nullable' => false,
'onDelete' => 'CASCADE',
],
],
];
}
/**
* @param string $reviewerEntity
* @param ClassMetadata $reviewerEntityMetadata
*
* @return array
*/
private function createReviewerMapping($reviewerEntity, ClassMetadata $reviewerEntityMetadata)
{
return [
'fieldName' => 'author',
'targetEntity' => $reviewerEntity,
'joinColumn' => [
'name' => 'author_id',
'referencedColumnName' => $reviewerEntityMetadata->fieldMappings['id']['columnName'],
'nullable' => false,
'onDelete' => 'CASCADE',
],
'cascade' => ['persist'],
];
}
/**
* @param string $reviewEntity
*
* @return array
*/
private function createReviewsMapping($reviewEntity)
{
return [
'fieldName' => 'reviews',
'targetEntity' => $reviewEntity,
'mappedBy' => 'reviewSubject',
'cascade' => ['all'],
];
}
}

View file

@ -40,7 +40,7 @@ class ReviewCreateListener
public function ensureReviewHasAuthor(GenericEvent $event)
{
if (!($subject = $event->getSubject()) instanceof ReviewInterface) {
throw new UnexpectedTypeException($subject, 'Sylius\Component\Review\Model\ReviewInterface');
throw new UnexpectedTypeException($subject, ReviewInterface::class);
}
if (null !== $subject->getAuthor()) {

View file

@ -11,7 +11,7 @@
namespace Sylius\Bundle\ReviewBundle\EventListener;
use Sylius\Bundle\ReviewBundle\Updater\ReviewableAverageRatingUpdaterInterface;
use Sylius\Bundle\ReviewBundle\Updater\ReviewableRatingUpdaterInterface;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
@ -22,16 +22,16 @@ use Symfony\Component\EventDispatcher\GenericEvent;
class ReviewDeleteListener
{
/**
* @var ReviewableAverageRatingUpdaterInterface
* @var ReviewableRatingUpdaterInterface
*/
private $reviewableAverageRatingUpdater;
private $averageRatingUpdater;
/**
* @param ReviewableAverageRatingUpdaterInterface $reviewableAverageRatingUpdater
* @param ReviewableRatingUpdaterInterface $averageRatingUpdater
*/
public function __construct(ReviewableAverageRatingUpdaterInterface $reviewableAverageRatingUpdater)
public function __construct(ReviewableRatingUpdaterInterface $averageRatingUpdater)
{
$this->reviewableAverageRatingUpdater = $reviewableAverageRatingUpdater;
$this->averageRatingUpdater = $averageRatingUpdater;
}
/**
@ -39,12 +39,13 @@ class ReviewDeleteListener
*/
public function recalculateSubjectRating(GenericEvent $event)
{
if (!(($subject = $event->getSubject()) instanceof ReviewInterface)) {
throw new UnexpectedTypeException($subject, 'Sylius\Component\Review\Model\ReviewInterface');
$subject = $event->getSubject();
if (!$subject instanceof ReviewInterface) {
throw new UnexpectedTypeException($subject, ReviewInterface::class);
}
if (ReviewInterface::STATUS_ACCEPTED === $subject->getStatus()) {
$this->reviewableAverageRatingUpdater->update($subject->getReviewSubject());
$this->averageRatingUpdater->update($subject->getReviewSubject());
}
}
}

View file

@ -1,55 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ReviewBundle\Form\Transformer;
use Sylius\Component\Core\Model\Customer;
use Sylius\Component\Review\Model\ReviewerInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
*/
class ReviewerTransformer implements DataTransformerInterface
{
/**
* {@inheritdoc}
*/
public function transform($value)
{
if (null === $value) {
return;
}
if (!$value instanceof ReviewerInterface) {
throw new UnexpectedTypeException($value, 'Sylius\Component\Review\Model\ReviewerInterface');
}
return $value->getEmail();
}
/**
* {@inheritdoc}
*/
public function reverseTransform($value)
{
if (!is_string($value)) {
return;
}
//this is temporary, after implementing proper form from UserBundle in ReviewType, it's going to be Reviewer object, as it's default review author class
$reviewer = new Customer();
$reviewer->setEmail($value);
return $reviewer;
}
}

View file

@ -13,11 +13,12 @@ namespace Sylius\Bundle\ReviewBundle\Form\Type;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @author Daniel Richter <nexyz9@gmail.com>
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ReviewType extends AbstractResourceType
{
@ -33,9 +34,9 @@ class ReviewType extends AbstractResourceType
*/
public function __construct($dataClass, array $validationGroups = [], $subject)
{
$this->subject = $subject;
parent::__construct($dataClass, $validationGroups);
$this->subject = $subject;
}
/**
@ -65,7 +66,7 @@ class ReviewType extends AbstractResourceType
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'rating_steps' => 5,

View file

@ -1,4 +1,4 @@
SyliusReviewBundle [![Build status...](https://secure.travis-ci.org/Sylius/SyliusReportBundle.png?branch=master)](http://travis-ci.org/Sylius/SyliusReportBundle)
SyliusReviewBundle [![Build status...](https://secure.travis-ci.org/Sylius/SyliusReviewBundle.png?branch=master)](http://travis-ci.org/Sylius/SyliusReviewBundle)
====================
Sylius
@ -21,18 +21,7 @@ Documentation
Contributing
------------
All informations about contributing to Sylius can be found on [this page](http://docs.sylius.org/en/latest/contributing/index.html).
Mailing lists
-------------
### Users
Questions? Feel free to ask on [users mailing list](http://groups.google.com/group/sylius).
### Developers
To contribute and develop this bundle, use the [developers mailing list](http://groups.google.com/group/sylius-dev).
All information's about contributing to Sylius can be found on [this page](http://docs.sylius.org/en/latest/contributing/index.html).
Sylius twitter account
----------------------
@ -62,10 +51,10 @@ This versioning method is same for all **Sylius** bundles and applications.
MIT License
-----------
License can be found [here](https://github.com/Sylius/SyliusReportBundle/blob/master/Resources/meta/LICENSE).
License can be found [here](https://github.com/Sylius/SyliusReviewBundle/blob/master/Resources/meta/LICENSE).
Authors
-------
The bundle was originally created by [Mateusz Zalewski](mateusz.zalewski@lakion.com), based on [Daniel Richter](nexyz9@gmail.com) work.
The bundle was originally created by [Mateusz Zalewski](mateusz.zalewski@lakion.com) and [Grzegorz Sadowski](grzegorz.sadowski@lakion.com), based on [Daniel Richter](nexyz9@gmail.com) work.
See the list of [contributors](https://github.com/Sylius/SyliusReviewBundle/contributors).

View file

@ -9,21 +9,19 @@
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\CoreBundle\EventListener;
namespace Sylius\Bundle\ReviewBundle\Remover;
use Doctrine\Common\Persistence\ObjectManager;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Sylius\Component\Review\Calculator\AverageRatingCalculatorInterface;
use Sylius\Component\Review\Calculator\ReviewableRatingCalculatorInterface;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewerInterface;
use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class CustomerDeleteListener
class ReviewerReviewsRemover implements ReviewerReviewsRemoverInterface
{
/**
* @var EntityRepository
@ -36,32 +34,30 @@ class CustomerDeleteListener
private $reviewManager;
/**
* @var AverageRatingCalculatorInterface
* @var ReviewableRatingCalculatorInterface
*/
private $averageRatingCalculator;
/**
* @param EntityRepository $reviewRepository
* @param ObjectManager $reviewManager
* @param AverageRatingCalculatorInterface $averageRatingCalculator
* @param EntityRepository $reviewRepository
* @param ObjectManager $reviewManager
* @param ReviewableRatingCalculatorInterface $averageRatingCalculator
*/
public function __construct(EntityRepository $reviewRepository, ObjectManager $reviewManager, AverageRatingCalculatorInterface $averageRatingCalculator)
{
public function __construct(
EntityRepository $reviewRepository,
ObjectManager $reviewManager,
ReviewableRatingCalculatorInterface $averageRatingCalculator
) {
$this->reviewRepository = $reviewRepository;
$this->reviewManager = $reviewManager;
$this->averageRatingCalculator = $averageRatingCalculator;
}
/**
* @param GenericEvent $event
* {@inheritdoc}
*/
public function removeCustomerReviews(GenericEvent $event)
public function removeReviewerReviews(ReviewerInterface $author)
{
$author = $event->getSubject();
if (!$author instanceof CustomerInterface) {
throw new UnexpectedTypeException($author, 'Sylius\Component\Core\Model\CustomerInterface');
}
$reviewSubjectsToRecalculate = [];
foreach ($this->reviewRepository->findBy(['author' => $author]) as $review) {
@ -75,7 +71,7 @@ class CustomerDeleteListener
}
/**
* @param ReviewInterface $review
* @param ReviewInterface $review
* @param ReviewableInterface[] $reviewSubjectsToRecalculate
*
* @return array

View file

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ReviewBundle\Remover;
use Sylius\Component\Review\Model\ReviewerInterface;
/**
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
interface ReviewerReviewsRemoverInterface
{
/**
* @param ReviewerInterface $author
*/
public function removeReviewerReviews(ReviewerInterface $author);
}

View file

@ -16,17 +16,17 @@
<mapped-superclass name="Sylius\Component\Review\Model\Review" table="sylius_review">
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
<generator strategy="AUTO" />
</id>
<field name="title" type="string" column="title"/>
<field name="rating" type="integer" column="rating"/>
<field name="title" type="string" column="title" />
<field name="rating" type="integer" column="rating" />
<field name="comment" type="text" column="comment" nullable="true" />
<field name="status" type="string" column="status" length="255"/>
<field name="status" type="string" column="status" length="255" />
<field name="createdAt" column="created_at" type="datetime">
<gedmo:timestampable on="create"/>
<gedmo:timestampable on="create" />
</field>
<field name="updatedAt" column="updated_at" type="datetime" nullable="true">
<gedmo:timestampable on="update"/>
<gedmo:timestampable on="update" />
</field>
</mapped-superclass>

View file

@ -15,11 +15,11 @@
<mapped-superclass name="Sylius\Component\Review\Model\Reviewer" table="sylius_reviewer">
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
<generator strategy="AUTO" />
</id>
<field name="email" type="string" column="email"/>
<field name="firstName" type="string" column="first_name"/>
<field name="lastName" type="string" column="last_name"/>
<field name="email" type="string" column="email" />
<field name="firstName" type="string" column="first_name" />
<field name="lastName" type="string" column="last_name" />
</mapped-superclass>
</doctrine-mapping>

View file

@ -14,34 +14,50 @@
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sylius.event_subscriber.review.load_metadata.class">Sylius\Bundle\ReviewBundle\EventListener\LoadMetadataSubscriber</parameter>
<parameter key="sylius.review.validator.unique_reviewer_email.class">Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueReviewerEmailValidator</parameter>
<parameter key="sylius.listener.review_create.class">Sylius\Bundle\ReviewBundle\EventListener\ReviewCreateListener</parameter>
<parameter key="sylius.listener.review_delete.class">Sylius\Bundle\ReviewBundle\EventListener\ReviewDeleteListener</parameter>
<parameter key="sylius.review.calculator.average_rating.class">Sylius\Component\Review\Calculator\AverageRatingCalculator</parameter>
<parameter key="sylius.review.updater.average_rating.class">Sylius\Bundle\ReviewBundle\Updater\AverageRatingUpdater</parameter>
<parameter key="sylius.review.remover.reviewer_reviews.class">Sylius\Bundle\ReviewBundle\Remover\ReviewerReviewsRemover</parameter>
</parameters>
<services>
<!--Subscribers-->
<service id="sylius.event_subscriber.review.load_metadata" class="Sylius\Bundle\ReviewBundle\EventListener\LoadMetadataSubscriber">
<service id="sylius.event_subscriber.review.load_metadata" class="%sylius.event_subscriber.review.load_metadata.class%">
<argument>%sylius.review.subjects%</argument>
<tag name="doctrine.event_subscriber" />
</service>
<!--Validators-->
<service id="sylius.review.validator.unique_user_email" class="Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueUserEmailValidator">
<service id="sylius.review.validator.unique_reviewer_email" class="%sylius.review.validator.unique_reviewer_email.class%">
<argument type="service" id="sylius.repository.user" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.authorization_checker" />
<tag name="validator.constraint_validator" alias="unique_user_email_validator" />
<tag name="validator.constraint_validator" alias="sylius_unique_reviewer_email_validator" />
</service>
<!--Listeners-->
<service id="sylius.listener.review_create" class="Sylius\Bundle\ReviewBundle\EventListener\ReviewCreateListener">
<service id="sylius.listener.review_create" class="%sylius.listener.review_create.class%">
<argument type="service" id="sylius.context.customer" />
<tag name="kernel.event_listener" event="sylius.product_review.pre_create" method="ensureReviewHasAuthor" />
</service>
<service id="sylius.listener.review_delete" class="Sylius\Bundle\ReviewBundle\EventListener\ReviewDeleteListener">
<argument type="service" id="sylius.review.updater.reviewable_average_rating" />
<service id="sylius.listener.review_delete" class="%sylius.listener.review_delete.class%">
<argument type="service" id="sylius.review.updater.average_rating" />
</service>
<service id="sylius.review.calculator.average_rating" class="Sylius\Component\Review\Calculator\AverageRatingCalculator" />
<service id="sylius.review.updater.reviewable_average_rating" class="Sylius\Bundle\ReviewBundle\Updater\ReviewableAverageRatingUpdater">
<service id="sylius.review.calculator.average_rating" class="%sylius.review.calculator.average_rating.class%" />
<service id="sylius.review.updater.average_rating" class="%sylius.review.updater.average_rating.class%">
<argument type="service" id="sylius.review.calculator.average_rating" />
<argument type="service" id="doctrine.orm.default_entity_manager" />
</service>
<service id="sylius.review.remover.reviewer_reviews" class="%sylius.review.remover.reviewer_reviews.class%">
<argument type="service" id="sylius.repository.product_review" />
<argument type="service" id="sylius.manager.product_review" />
<argument type="service" id="sylius.review.calculator.average_rating" />
</service>
</services>
</container>

View file

@ -42,7 +42,7 @@
</option>
</constraint>
</property>
<constraint name="Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueUserEmail" >
<constraint name="Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueReviewerEmail" >
<option name="groups">sylius_review</option>
</constraint>
</class>

View file

@ -1,4 +1,4 @@
Copyright (c) 2011-2015 Paweł Jędrzejewski
Copyright (c) 2011-2016 Paweł Jędrzejewski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -7,40 +7,57 @@
* file that was distributed with this source code.
*/
$(document).ready(function(){
$("#reviewForm").submit(function(e){
function initializeAjaxForm(successMessage) {
$("#reviewForm").submit(function(e) {
e.preventDefault();
var form = $(this);
$.ajax({
type: "POST",
url: location.href+"/product_review",
data: $(this).serialize(),
url: form.attr('action'),
data: parseFormToJson(form),
dataType: "json",
success: function(data) {
if (data == 'success') {
location.reload();
} else {
renderErrorsOnProperFields(data, form);
}
accept: "application/json",
success: function(data, textStatus, xhr) {
completeRequest(form);
appendFlash(successMessage);
},
error: function(xhr, textStatus, errorThrown) {
renderErrors(xhr, form);
}
});
});
});
}
function renderErrorsOnProperFields(data, form) {
function renderErrors(xhr, form) {
clearErrors(form);
$.each(data, function(key, value){
var element = form.find('#sylius_product_review_'+key.replace('data.', ''));
element.siblings('.form-error').remove();
element.parent().append('<span class="help-block form-error">'+value+'</span>');
element.parents('.form-group').addClass('has-error');
$.each(xhr.responseJSON.errors.errors, function(key, value) {
$('div.panel-body').addClass('has-error').prepend('<span class="help-block form-error">' + value + '</span>');
});
}
function clearErrors(form) {
form.find('.form-error').remove();
form.find('.has-error').removeClass('has-error');
}
}
function parseFormToJson(form) {
var formJson = {};
$.each(form.serializeArray(), function(index, field) {
var name = field.name.replace('sylius_product_review[', '').replace(']', '');
formJson[name] = field.value || '';
});
return formJson;
}
function appendFlash(successMessage) {
$('<div class="alert alert-success"><a class="close" data-dismiss="alert" href="#">×</a>' + successMessage + '</div>').insertAfter('h1.logo');
}
function completeRequest(form) {
form[0].reset();
clearErrors(form);
$("html, body").animate({ scrollTop: 0 }, "slow");
}

View file

@ -2,9 +2,6 @@
# (c) Paweł Jędrzejewski
sylius:
review:
header: Product reviews
no_results: There are no reviews for this product.
form:
review:
header: Add a review
@ -18,4 +15,3 @@ sylius:
approved: Approved
rejected: Rejected
submit: Submit
product: Product

View file

@ -7,4 +7,4 @@ sylius:
rating:
not_blank: You must check review rating.
author:
already_exists: This email is already registered. Please log in.
already_exists: This email is already registered, please login or use forgotten password.

View file

@ -13,9 +13,12 @@ namespace Sylius\Bundle\ReviewBundle;
use Sylius\Bundle\ResourceBundle\AbstractResourceBundle;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Sylius\Bundle\ReviewBundle\DependencyInjection\Compiler\RegisterReviewFactoryPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class SyliusReviewBundle extends AbstractResourceBundle
{
@ -29,6 +32,16 @@ class SyliusReviewBundle extends AbstractResourceBundle
];
}
/**
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new RegisterReviewFactoryPass());
}
/**
* {@inheritdoc}
*/

View file

@ -12,17 +12,18 @@
namespace Sylius\Bundle\ReviewBundle\Updater;
use Doctrine\Common\Persistence\ObjectManager;
use Sylius\Component\Review\Calculator\AverageRatingCalculatorInterface;
use Sylius\Component\Review\Calculator\ReviewableRatingCalculatorInterface;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ReviewableAverageRatingUpdater implements ReviewableAverageRatingUpdaterInterface
class AverageRatingUpdater implements ReviewableRatingUpdaterInterface
{
/**
* @var AverageRatingCalculatorInterface
* @var ReviewableRatingCalculatorInterface
*/
private $averageRatingCalculator;
@ -32,10 +33,10 @@ class ReviewableAverageRatingUpdater implements ReviewableAverageRatingUpdaterIn
private $reviewSubjectManager;
/**
* @param AverageRatingCalculatorInterface $averageRatingCalculator
* @param ObjectManager $reviewSubjectManager
* @param ReviewableRatingCalculatorInterface $averageRatingCalculator
* @param ObjectManager $reviewSubjectManager
*/
public function __construct(AverageRatingCalculatorInterface $averageRatingCalculator, ObjectManager $reviewSubjectManager)
public function __construct(ReviewableRatingCalculatorInterface $averageRatingCalculator, ObjectManager $reviewSubjectManager)
{
$this->averageRatingCalculator = $averageRatingCalculator;
$this->reviewSubjectManager = $reviewSubjectManager;

View file

@ -16,8 +16,9 @@ use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
interface ReviewableAverageRatingUpdaterInterface
interface ReviewableRatingUpdaterInterface
{
/**
* @param ReviewableInterface $reviewSubject

View file

@ -15,8 +15,9 @@ use Symfony\Component\Validator\Constraint;
/**
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class UniqueUserEmail extends Constraint
class UniqueReviewerEmail extends Constraint
{
/**
* @var string
@ -28,7 +29,7 @@ class UniqueUserEmail extends Constraint
*/
public function validatedBy()
{
return 'unique_user_email_validator';
return 'sylius_unique_reviewer_email_validator';
}
/**

View file

@ -11,9 +11,10 @@
namespace Sylius\Bundle\ReviewBundle\Validator\Constraints;
use Sylius\Bundle\UserBundle\Doctrine\ORM\UserRepository;
use Sylius\Component\User\Model\UserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@ -22,7 +23,7 @@ use Symfony\Component\Validator\ConstraintValidator;
* @author Mateusz Zalewski <mateusz.p.zalewski@gmail.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class UniqueUserEmailValidator extends ConstraintValidator
class UniqueReviewerEmailValidator extends ConstraintValidator
{
/**
* @var UserRepository
@ -40,12 +41,15 @@ class UniqueUserEmailValidator extends ConstraintValidator
private $authorizationChecker;
/**
* @param UserRepository $userRepository
* @param UserRepositoryInterface $userRepository
* @param TokenStorageInterface $tokenStorage
* @param AuthorizationCheckerInterface $authorizationChecker
*/
public function __construct(UserRepository $userRepository, TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authorizationChecker)
{
public function __construct(
UserRepositoryInterface $userRepository,
TokenStorageInterface $tokenStorage,
AuthorizationCheckerInterface $authorizationChecker
) {
$this->userRepository = $userRepository;
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
@ -59,7 +63,7 @@ class UniqueUserEmailValidator extends ConstraintValidator
$customer = $review->getAuthor();
$token = $this->tokenStorage->getToken();
if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED') && $token->getUser() instanceof UserInterface) {
if ($this->checkIfUserIsAuthenticated($token)) {
if (null !== $customer && $token->getUser()->getCustomer()->getEmail() === $customer->getEmail()) {
return;
}
@ -74,4 +78,18 @@ class UniqueUserEmailValidator extends ConstraintValidator
);
}
}
/**
* @param TokenInterface $token
*
* @return bool
*/
private function checkIfUserIsAuthenticated(TokenInterface $token)
{
return
null !== $token &&
$this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED') &&
$token->getUser() instanceof UserInterface
;
}
}

View file

@ -7,36 +7,42 @@
"license": "MIT",
"authors": [
{
"name": "Paweł Jędrzejewski",
"name": "Paweł Jędrzejewski",
"homepage": "http://pjedrzejewski.com"
},
{
"name": "Mateusz Zalewski",
"email": "mateusz.zalewski@lakion.com"
"name": "Mateusz Zalewski",
"email": "mateusz.zalewski@lakion.com"
},
{
"name": "Daniel Richter",
"email": "nexyz9@gmail.com"
"name": "Grzegorz Sadowski",
"email": "grzegorz.sadowski@lakion.com"
},
{
"name": "Sylius project",
"name": "Daniel Richter",
"email": "nexyz9@gmail.com"
},
{
"name": "Sylius project",
"homepage": "http://sylius.org"
},
{
"name": "Community contributions",
"name": "Community contributions",
"homepage": "http://github.com/Sylius/Sylius/contributors"
}
],
"require": {
"php": ">=5.5.9",
"symfony/framework-bundle": "^2.7",
"php": "^5.5.9|^7.0",
"symfony/framework-bundle": "^2.7.7",
"stof/doctrine-extensions-bundle": "~1.1",
"sylius/resource-bundle": "0.16.*",
"sylius/review": "0.16.*"
"sylius/review": "0.17.*",
"sylius/user-bundle": "0.17.*",
"sylius/resource-bundle": "0.17.*"
},
"require-dev": {
"phpspec/phpspec": "~2.1",
"symfony/form": "^2.7",
"phpspec/phpspec": "^2.4",
"symfony/form": "^2.7",
"symfony/validator": "^2.7"
},
"config": {
@ -48,9 +54,10 @@
"autoload-dev": {
"psr-4": { "Sylius\\Bundle\\ReviewBundle\\spec\\": "spec/" }
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "0.16-dev"
"dev-master": "0.17-dev"
}
}
}

View file

@ -11,6 +11,7 @@
namespace spec\Sylius\Bundle\ReviewBundle\EventListener;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
@ -50,7 +51,7 @@ class LoadMetadataSubscriberSpec extends ObjectBehavior
function it_implements_event_subscriber()
{
$this->shouldImplement('Doctrine\Common\EventSubscriber');
$this->shouldImplement(EventSubscriber::class);
}
function it_has_subscribed_events()
@ -93,7 +94,7 @@ class LoadMetadataSubscriberSpec extends ObjectBehavior
'fieldName' => 'author',
'targetEntity' => 'AcmeBundle\Entity\ReviewerModel',
'joinColumn' => [
'name' => 'customer_id',
'name' => 'author_id',
'referencedColumnName' => 'id',
'nullable' => false,
'onDelete' => 'CASCADE',

View file

@ -40,8 +40,8 @@ class ReviewCreateListenerSpec extends ObjectBehavior
GenericEvent $event,
ReviewInterface $review
) {
$event->getSubject()->willReturn($review)->shouldBeCalled();
$customerContext->getCustomer()->willReturn($customer)->shouldBeCalled();
$event->getSubject()->willReturn($review);
$customerContext->getCustomer()->willReturn($customer);
$review->getAuthor()->willReturn(null);
$review->setAuthor($customer)->shouldBeCalled();
@ -53,7 +53,7 @@ class ReviewCreateListenerSpec extends ObjectBehavior
{
$event->getSubject()->willReturn('badObject')->shouldBeCalled();
$this->shouldThrow(new UnexpectedTypeException('badObject', 'Sylius\Component\Review\Model\ReviewInterface'))->during('ensureReviewHasAuthor', [$event]);
$this->shouldThrow(new UnexpectedTypeException('badObject', ReviewInterface::class))->during('ensureReviewHasAuthor', [$event]);
}
function it_does_nothing_if_review_already_has_author(
@ -62,8 +62,8 @@ class ReviewCreateListenerSpec extends ObjectBehavior
GenericEvent $event,
ReviewInterface $review
) {
$event->getSubject()->willReturn($review)->shouldBeCalled();
$review->getAuthor()->willReturn($existingAuthor)->shouldBeCalled();
$event->getSubject()->willReturn($review);
$review->getAuthor()->willReturn($existingAuthor);
$customerContext->getCustomer()->shouldNotBeCalled();

View file

@ -13,7 +13,7 @@ namespace spec\Sylius\Bundle\ReviewBundle\EventListener;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\ReviewBundle\Updater\ReviewableAverageRatingUpdaterInterface;
use Sylius\Bundle\ReviewBundle\Updater\ReviewableRatingUpdaterInterface;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewInterface;
@ -24,9 +24,9 @@ use Symfony\Component\EventDispatcher\GenericEvent;
*/
class ReviewDeleteListenerSpec extends ObjectBehavior
{
function let(ReviewableAverageRatingUpdaterInterface $reviewableAverageRatingUpdater)
function let(ReviewableRatingUpdaterInterface $averageRatingUpdater)
{
$this->beConstructedWith($reviewableAverageRatingUpdater);
$this->beConstructedWith($averageRatingUpdater);
}
function it_is_initializable()
@ -34,33 +34,37 @@ class ReviewDeleteListenerSpec extends ObjectBehavior
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\EventListener\ReviewDeleteListener');
}
function it_recalculates_subject_rating_on_accepted_review_deletion($reviewableAverageRatingUpdater, GenericEvent $event, ReviewInterface $review, ReviewableInterface $reviewSubject)
{
$event->getSubject()->willReturn($review)->shouldBeCalled();
$review->getStatus()->willReturn(ReviewInterface::STATUS_ACCEPTED)->shouldBeCalled();
$review->getReviewSubject()->willReturn($reviewSubject)->shouldBeCalled();
function it_recalculates_subject_rating_on_accepted_review_deletion(
$averageRatingUpdater,
GenericEvent $event,
ReviewInterface $review,
ReviewableInterface $reviewSubject
) {
$event->getSubject()->willReturn($review);
$review->getStatus()->willReturn(ReviewInterface::STATUS_ACCEPTED);
$review->getReviewSubject()->willReturn($reviewSubject);
$reviewableAverageRatingUpdater->update($reviewSubject)->shouldBeCalled();
$averageRatingUpdater->update($reviewSubject)->shouldBeCalled();
$this->recalculateSubjectRating($event);
}
function it_does_nothing_if_review_was_new($reviewableAverageRatingUpdater, GenericEvent $event, ReviewInterface $review)
function it_does_nothing_if_review_was_new($averageRatingUpdater, GenericEvent $event, ReviewInterface $review)
{
$event->getSubject()->willReturn($review)->shouldBeCalled();
$review->getStatus()->willReturn(ReviewInterface::STATUS_NEW)->shouldBeCalled();
$event->getSubject()->willReturn($review);
$review->getStatus()->willReturn(ReviewInterface::STATUS_NEW);
$reviewableAverageRatingUpdater->update(Argument::type('Sylius\Component\Review\Model\ReviewableInterface'))->shouldNotBeCalled();
$averageRatingUpdater->update(Argument::type(ReviewableInterface::class))->shouldNotBeCalled();
$this->recalculateSubjectRating($event);
}
function it_does_nothing_if_review_was_rejected($reviewableAverageRatingUpdater, GenericEvent $event, ReviewInterface $review)
function it_does_nothing_if_review_was_rejected($averageRatingUpdater, GenericEvent $event, ReviewInterface $review)
{
$event->getSubject()->willReturn($review)->shouldBeCalled();
$review->getStatus()->willReturn(ReviewInterface::STATUS_REJECTED)->shouldBeCalled();
$event->getSubject()->willReturn($review);
$review->getStatus()->willReturn(ReviewInterface::STATUS_REJECTED);
$reviewableAverageRatingUpdater->update(Argument::type('Sylius\Component\Review\Model\ReviewableInterface'))->shouldNotBeCalled();
$averageRatingUpdater->update(Argument::type(ReviewableInterface::class))->shouldNotBeCalled();
$this->recalculateSubjectRating($event);
}
@ -70,7 +74,7 @@ class ReviewDeleteListenerSpec extends ObjectBehavior
$event->getSubject()->willReturn('badObject');
$this
->shouldThrow(new UnexpectedTypeException('badObject', 'Sylius\Component\Review\Model\ReviewInterface'))
->shouldThrow(new UnexpectedTypeException('badObject', ReviewInterface::class))
->during('recalculateSubjectRating', [$event])
;
}

View file

@ -1,75 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\Sylius\Bundle\ReviewBundle\Form\Transformer;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Review\Model\ReviewerInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
class ReviewerTransformerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Form\Transformer\ReviewerTransformer');
}
function it_implements_data_transformer_interface()
{
$this->shouldImplement('Symfony\Component\Form\DataTransformerInterface');
}
function it_transforms_form_data(ReviewerInterface $author)
{
$author->getEmail()->willReturn('john.doe@example.com');
$this->transform($author)->shouldReturn('john.doe@example.com');
}
function it_returns_null_if_given_value_is_null()
{
$this->transform(null)->shouldReturn(null);
}
function it_throws_exception_if_given_value_is_not_reviewer_interface_object()
{
$this->shouldThrow(new UnexpectedTypeException('badObject', 'Sylius\Component\Review\Model\ReviewerInterface'))->during('transform', ['badObject']);
}
function it_reverse_transforms_form_data(ReviewerInterface $author)
{
$author->getEmail()->willReturn('john.doe@example.com');
$this->reverseTransform('john.doe@example.com')->shouldBeSameAs($author);
}
function it_returns_null_if_given_value_is_incorrect(\DateTime $wrongObject)
{
$this->reverseTransform(null)->shouldReturn(null);
$this->reverseTransform($wrongObject)->shouldReturn(null);
}
public function getMatchers()
{
return [
'beSameAs' => function ($subject, $key) {
if (!$subject instanceof ReviewerInterface || !$key instanceof ReviewerInterface) {
return false;
}
return $subject->getEmail() === $key->getEmail();
},
];
}
}

View file

@ -12,11 +12,14 @@
namespace spec\Sylius\Bundle\ReviewBundle\Form\Type;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ReviewTypeSpec extends ObjectBehavior
{
@ -32,42 +35,31 @@ class ReviewTypeSpec extends ObjectBehavior
function it_is_abstract_type_object()
{
$this->shouldHaveType('Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType');
$this->shouldHaveType(AbstractResourceType::class);
}
function it_builds_form(FormBuilderInterface $builder)
{
$builder
->add('rating', 'choice', [
'choices' => [1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5],
'label' => 'sylius.form.review.rating',
'expanded' => true,
'multiple' => false,
])
->add('rating', 'choice', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('author', 'sylius_customer_guest', [
'label' => false,
])
->add('author', 'sylius_customer_guest', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('title', 'text', [
'label' => 'sylius.form.review.title',
])
->add('title', 'text', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
$builder
->add('comment', 'textarea', [
'label' => 'sylius.form.review.comment',
])
->add('comment', 'textarea', Argument::cetera())
->willReturn($builder)
->shouldBeCalled()
;
@ -75,7 +67,7 @@ class ReviewTypeSpec extends ObjectBehavior
$this->buildForm($builder, ['rating_steps' => 5]);
}
function it_sets_default_options(OptionsResolverInterface $resolver)
function it_configures_options(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
@ -84,7 +76,7 @@ class ReviewTypeSpec extends ObjectBehavior
]
)->shouldBeCalled();
$this->setDefaultOptions($resolver);
$this->configureOptions($resolver);
}
function it_has_name()

View file

@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\Sylius\Bundle\ReviewBundle\Remover;
use Doctrine\Common\Persistence\ObjectManager;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Bundle\ReviewBundle\Remover\ReviewerReviewsRemoverInterface;
use Sylius\Component\Review\Calculator\ReviewableRatingCalculatorInterface;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewerInterface;
use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ReviewerReviewsRemoverSpec extends ObjectBehavior
{
function let(
EntityRepository $reviewRepository,
ObjectManager $reviewManager,
ReviewableRatingCalculatorInterface $averageRatingCalculator
) {
$this->beConstructedWith($reviewRepository, $reviewManager, $averageRatingCalculator);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Remover\ReviewerReviewsRemover');
}
function it_implements_reviewer_reviews_remover_interface()
{
$this->shouldImplement(ReviewerReviewsRemoverInterface::class);
}
function it_removes_soft_deleted_customer_reviews_and_recalculates_their_product_ratings(
$averageRatingCalculator,
$reviewRepository,
$reviewManager,
ReviewerInterface $author,
ReviewableInterface $reviewSubject,
ReviewInterface $review
) {
$reviewRepository->findBy(['author' => $author])->willReturn([$review]);
$review->getReviewSubject()->willReturn($reviewSubject);
$reviewManager->remove($review)->shouldBeCalled();
$reviewManager->flush()->shouldBeCalled();
$averageRatingCalculator->calculate($reviewSubject)->willReturn(0);
$reviewSubject->setAverageRating(0)->shouldBeCalled();
$this->removeReviewerReviews($author);
}
}

View file

@ -13,28 +13,29 @@ namespace spec\Sylius\Bundle\ReviewBundle\Updater;
use Doctrine\Common\Persistence\ObjectManager;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Review\Calculator\AverageRatingCalculatorInterface;
use Sylius\Bundle\ReviewBundle\Updater\ReviewableRatingUpdaterInterface;
use Sylius\Component\Review\Calculator\ReviewableRatingCalculatorInterface;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
class ReviewableAverageRatingUpdaterSpec extends ObjectBehavior
class AverageRatingUpdaterSpec extends ObjectBehavior
{
function let(AverageRatingCalculatorInterface $averageRatingCalculator, ObjectManager $reviewSubjectManager)
function let(ReviewableRatingCalculatorInterface $averageRatingCalculator, ObjectManager $reviewSubjectManager)
{
$this->beConstructedWith($averageRatingCalculator, $reviewSubjectManager);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Updater\ReviewableAverageRatingUpdater');
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Updater\AverageRatingUpdater');
}
function it_implements_product_average_rating_updater_interface()
{
$this->shouldImplement('Sylius\Bundle\ReviewBundle\Updater\ReviewableAverageRatingUpdaterInterface');
$this->shouldImplement(ReviewableRatingUpdaterInterface::class);
}
function it_updates_review_subject_average_rating(
@ -42,7 +43,7 @@ class ReviewableAverageRatingUpdaterSpec extends ObjectBehavior
$reviewSubjectManager,
ReviewableInterface $reviewSubject
) {
$averageRatingCalculator->calculate($reviewSubject)->willReturn(4.5)->shouldBeCalled();
$averageRatingCalculator->calculate($reviewSubject)->willReturn(4.5);
$reviewSubject->setAverageRating(4.5)->shouldBeCalled();
$reviewSubjectManager->flush($reviewSubject)->shouldBeCalled();
@ -56,8 +57,8 @@ class ReviewableAverageRatingUpdaterSpec extends ObjectBehavior
ReviewableInterface $reviewSubject,
ReviewInterface $review
) {
$review->getReviewSubject()->willReturn($reviewSubject)->shouldBeCalled();
$averageRatingCalculator->calculate($reviewSubject)->willReturn(4.5)->shouldBeCalled();
$review->getReviewSubject()->willReturn($reviewSubject);
$averageRatingCalculator->calculate($reviewSubject)->willReturn(4.5);
$reviewSubject->setAverageRating(4.5)->shouldBeCalled();
$reviewSubjectManager->flush($reviewSubject)->shouldBeCalled();

View file

@ -18,16 +18,16 @@ use Symfony\Component\Validator\Constraint;
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class UniqueUserEmailSpec extends ObjectBehavior
class UniqueReviewerEmailSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueUserEmail');
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueReviewerEmail');
}
function it_extends_contraint_class()
{
$this->shouldHaveType('Symfony\Component\Validator\Constraint');
$this->shouldHaveType(Constraint::class);
}
function it_has_message()
@ -37,7 +37,7 @@ class UniqueUserEmailSpec extends ObjectBehavior
function it_is_validate_by_unique_user_email_validator()
{
$this->validatedBy()->shouldReturn('unique_user_email_validator');
$this->validatedBy()->shouldReturn('sylius_unique_reviewer_email_validator');
}
function it_has_targets()

View file

@ -12,35 +12,40 @@
namespace spec\Sylius\Bundle\ReviewBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueUserEmail;
use Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueReviewerEmail;
use Sylius\Bundle\UserBundle\Doctrine\ORM\UserRepository;
use Sylius\Component\Review\Model\ReviewInterface;
use Sylius\Component\User\Model\CustomerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\ExecutionContext;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class UniqueUserEmailValidatorSpec extends ObjectBehavior
class UniqueReviewerEmailValidatorSpec extends ObjectBehavior
{
function let(UserRepository $userRepository, TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authorizationChecker, ExecutionContext $context)
{
function let(
UserRepository $userRepository,
TokenStorageInterface $tokenStorage,
AuthorizationCheckerInterface $authorizationChecker,
ExecutionContext $context
) {
$this->beConstructedWith($userRepository, $tokenStorage, $authorizationChecker);
$this->initialize($context);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueUserEmailValidator');
$this->shouldHaveType('Sylius\Bundle\ReviewBundle\Validator\Constraints\UniqueReviewerEmailValidator');
}
function it_extends_contraint_validator_class()
{
$this->shouldHaveType('Symfony\Component\Validator\ConstraintValidator');
$this->shouldHaveType(ConstraintValidator::class);
}
function it_validates_if_user_with_given_email_is_already_registered(
@ -49,7 +54,7 @@ class UniqueUserEmailValidatorSpec extends ObjectBehavior
$authorizationChecker,
$context,
TokenInterface $token,
UniqueUserEmail $constraint,
UniqueReviewerEmail $constraint,
ReviewInterface $review,
CustomerInterface $customer,
CustomerInterface $existingUser
@ -59,7 +64,7 @@ class UniqueUserEmailValidatorSpec extends ObjectBehavior
$review->getAuthor()->willReturn($customer);
$customer->getEmail()->willReturn('john.doe@example.com');
$userRepository->findOneByEmail('john.doe@example.com')->willReturn($existingUser)->shouldBeCalled();
$userRepository->findOneByEmail('john.doe@example.com')->willReturn($existingUser);
$constraint->message = 'This email is already registered. Please log in.';
$context->addViolationAt('author', 'This email is already registered. Please log in.', [], null)->shouldBeCalled();

View file

@ -28,8 +28,8 @@ class CustomerGuestType extends AbstractResourceType
private $guestCustomerSubscriber;
/**
* @param string $dataClass
* @param array $validationGroups
* @param string $dataClass
* @param array $validationGroups
* @param EventSubscriberInterface $guestCustomerSubscriber
*/
public function __construct($dataClass, array $validationGroups, EventSubscriberInterface $guestCustomerSubscriber)

View file

@ -33,4 +33,4 @@ sylius:
not_blank: Please enter your email.
invalid: This email is invalid.
unique: This email is already used.
registered: This email is already registered, please login or reset your password.
registered: This email is already registered, please login or use forgotten password.

View file

@ -1,194 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\WebBundle\Controller\Frontend\Review;
use Doctrine\Common\Persistence\ObjectManager;
use FOS\RestBundle\Controller\FOSRestController;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @author Justin Hilles <justin@1011i.com>
* @author Daniel Richter <nexyz9@gmail.com>
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ProductReviewController extends FOSRestController
{
/**
* @param Request $request
*
* @return Response
*/
public function indexAction(Request $request)
{
$product = $this->findProductOr404($request->attributes->get('slug'));
$reviews = $this->getProductReviewRepository()->findBy([
'reviewSubject' => $product,
'status' => ReviewInterface::STATUS_ACCEPTED,
]);
$view = $this
->view()
->setTemplate('SyliusWebBundle:Frontend/Review:_list.html.twig')
->setData([
'product_reviews' => $reviews,
])
;
return $this->handleView($view);
}
/**
* @return Response
*/
public function createAction()
{
$review = $this->getProductReviewFactory()->createNew();
$form = $this->getReviewForm($review);
$view = $this
->view()
->setTemplate('SyliusWebBundle:Frontend/Review:_form.html.twig')
->setData([
'product_review' => $review,
'form' => $form->createView(),
])
;
return $this->handleView($view);
}
/**
* @param Request $request
*
* @return JsonResponse
*/
public function createWithAjaxAction(Request $request)
{
$review = $this->getProductReviewFactory()->createNew();
$form = $this->getReviewForm($review);
if ($form->submit($request)->isValid()) {
$manager = $this->getProductReviewManager();
$review = $form->getData();
$product = $this->findProductOr404($request->attributes->get('slug'));
$review->setReviewSubject($product);
$manager->persist($review);
$manager->flush();
$this->addFlashMessage('success', 'sylius.resource.create', ['%resource%' => 'Product review']);
return new JsonResponse('success');
}
return new JsonResponse($this->getFormErrorsAsArray($form));
}
/**
* @param string $slug
*
* @return ProductInterface
*
* @throws NotFoundHttpException
*/
private function findProductOr404($slug)
{
if (!$product = $this->getProductRepository()->findOneBy(['slug' => $slug])) {
throw new NotFoundHttpException(sprintf('Product with slug "%s" not found', $slug));
}
return $product;
}
/**
* @param FormInterface $form
*
* @return array
*/
private function getFormErrorsAsArray(FormInterface $form)
{
$errors = [];
foreach ($form->getErrors(true) as $error) {
$errors[$error->getCause()->getPropertyPath()] = $error->getMessage();
}
return $errors;
}
/**
* @return FactoryInterface
*/
private function getProductReviewFactory()
{
return $this->get('sylius.factory.product_review');
}
/**
* @return EntityRepository
*/
private function getProductReviewRepository()
{
return $this->get('sylius.repository.product_review');
}
/**
* @return EntityRepository
*/
private function getProductRepository()
{
return $this->get('sylius.repository.product');
}
/**
* @param ReviewInterface $review
*
* @return FormInterface
*/
private function getReviewForm(ReviewInterface $review)
{
return $this->get('form.factory')->create('sylius_product_review', $review);
}
/**
* @return ObjectManager
*/
private function getProductReviewManager()
{
return $this->get('sylius.manager.product_review');
}
/**
* @param string $type
* @param string $message
* @param array $parameters
*
* @throws \LogicException
*/
private function addFlashMessage($type, $message, array $parameters)
{
if (!$this->container->has('session')) {
throw new \LogicException('You can not use the addFlash method if sessions are disabled.');
}
$translator = $this->container->get('translator');
$this->container->get('session')->getFlashBag()->add($type, $translator->trans($message, $parameters, 'flashes'));
}
}

View file

@ -374,8 +374,8 @@ class BackendMenuBuilder extends MenuBuilder
/**
* @param ItemInterface $menu
* @param array $childOptions
* @param string $section
* @param array $childOptions
* @param string $section
*/
public function addReviewsMenu(ItemInterface $menu, array $childOptions, $section)
{

View file

@ -183,6 +183,6 @@ sylius_backend_metadata_container:
resource: @SyliusWebBundle/Resources/config/routing/backend/metadata_container.yml
prefix: /metadata
sylius_backend_review:
resource: @SyliusWebBundle/Resources/config/routing/backend/review.yml
prefix: /reviews
sylius_backend_product_review:
resource: @SyliusWebBundle/Resources/config/routing/backend/product_review.yml
prefix: /product-reviews

View file

@ -48,3 +48,4 @@ sylius_backend_product_review_update_state:
defaults:
_controller: sylius.controller.product_review:updateStateAction
graph: sylius_product_review

View file

@ -29,8 +29,8 @@ sylius_user_account:
resource: @SyliusUserBundle/Resources/config/routing/account.yml
prefix: /account
sylius_review:
resource: @SyliusWebBundle/Resources/config/routing/frontend/review.yml
sylius_product_review:
resource: @SyliusWebBundle/Resources/config/routing/frontend/product_review.yml
sylius_cart:
resource: @SyliusCartBundle/Resources/config/routing.yml

View file

@ -0,0 +1,16 @@
# This file is part of the Sylius package.
# (c) Paweł Jędrzejewski
sylius_product_review:
path: /products/{productId}/reviews/new
methods: [POST]
defaults:
_controller: sylius.controller.product_review:createAction
_sylius:
template: 'SyliusWebBundle:Frontend/Review:_form.html.twig'
factory:
method: createForSubject
arguments: [$productId]
permission: false
form: sylius_product_review
_format: json

View file

@ -1,7 +0,0 @@
sylius_review_product:
pattern: /p/{slug}/product_review
methods: [POST]
defaults:
_controller: sylius.controller.frontend.product_review:createWithAjaxAction
_sylius:
permission: false

View file

@ -6,5 +6,5 @@ sylius_product:
prefix: /product
sylius_review:
resource: @SyliusWebBundle/Resources/config/routing/partial/review.yml
prefix: /reviews
resource: @SyliusWebBundle/Resources/config/routing/partial/product-review.yml
prefix: /product-reviews

View file

@ -0,0 +1,25 @@
# This file is part of the Sylius package.
# (c) Paweł Jędrzejewski
sylius_product_reviews:
path: /products/{productId}/reviews/
defaults:
_controller: sylius.controller.product_review:indexAction
_sylius:
template: 'SyliusWebBundle:Frontend/Review:_list.html.twig'
repository:
method: findAcceptedBySubjectId
arguments: [$productId]
permission: false
sylius_product_review_create:
path: /products/{productId}/reviews/new
methods: [GET]
defaults:
_controller: sylius.controller.product_review:createAction
_sylius:
template: 'SyliusWebBundle:Frontend/Review:_form.html.twig'
factory:
method: createForSubject
arguments: [$productId]
permission: false

View file

@ -1,25 +0,0 @@
# This file is part of the Sylius package.
# (c) Paweł Jędrzejewski
sylius_partial_product_review_list:
pattern: /{slug}
defaults:
_controller: sylius.controller.frontend.product_review:indexAction
_sylius:
paginate: false
limit: $limit
sorting:
createdAt: desc
filterable: true
criteria: { 'reviewSubject': $product, 'status': 'accepted' }
template: 'SyliusWebBundle:Frontend/Review:_list.html.twig'
permission: false
sylius_partial_product_review_form:
pattern: /{slug}/new
methods: [GET]
defaults:
_controller: sylius.controller.frontend.product_review:createAction
_sylius:
template: 'SyliusWebBundle:Frontend/Review:_form.html.twig'
permission: false

View file

@ -20,7 +20,6 @@
<parameter key="sylius.controller.frontend.homepage.class">Sylius\Bundle\WebBundle\Controller\Frontend\HomepageController</parameter>
<parameter key="sylius.controller.frontend.account.address.class">Sylius\Bundle\WebBundle\Controller\Frontend\Account\AddressController</parameter>
<parameter key="sylius.controller.frontend.account.order.class">Sylius\Bundle\WebBundle\Controller\Frontend\Account\OrderController</parameter>
<parameter key="sylius.controller.frontend.product_review.class">Sylius\Bundle\WebBundle\Controller\Frontend\Review\ProductReviewController</parameter>
<parameter key="sylius.controller.backend.dashboard.class">Sylius\Bundle\WebBundle\Controller\Backend\DashboardController</parameter>
<parameter key="sylius.controller.backend.security.class">Sylius\Bundle\WebBundle\Controller\Backend\SecurityController</parameter>
@ -32,7 +31,6 @@
</parameters>
<services>
<service id="sylius.controller.frontend.homepage" class="%sylius.controller.frontend.homepage.class%">
<call method="setContainer">
<argument type="service" id="service_container" />
@ -48,11 +46,6 @@
<argument type="service" id="service_container" />
</call>
</service>
<service id="sylius.controller.frontend.product_review" class="%sylius.controller.frontend.product_review.class%">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
<service id="sylius.controller.backend.dashboard" class="%sylius.controller.backend.dashboard.class%">
<call method="setContainer">
<argument type="service" id="service_container" />

View file

@ -675,52 +675,6 @@ sylius:
taxes: Taxes
total: Total
update_header: Editing order
customer: customer
currency: Currency
state: State
immutable_warning: Already shipped orders are immutable.
promotion_coupons: Coupons
no_promotion: No coupons were used.order:
adjustments:
manage: Manage adjustments
type: Type
description: Description
amount: Amount
locked: Locked?
lock: Lock
unlock: Unlock
lock_all: Lock all adjustments
unlock_all: Unlock all adjustments
comment:
header: Order comments
comment: Comment
author: Author
created_at: Created at
notify: Notification
state: State
customer:
notified: Customer notified
not_notified: Customer not notified
no_comment: No comment
route:
create: Create route
create_header: Creating route
id: ID
index_header: Routes
name: Name
no_results: There are no routes to display.
page: Page
title: Title
update_header: Editing route
view: View
redirect_route:
create: Create redirect route
create_header: Creating redirect route
id: ID
index_header: Redirect route
no_results: There are no redirect routes to display.
title: Title
update_header: Editing redirect route
review:
create: New review
index: Product reviews
@ -736,43 +690,9 @@ sylius:
created_at: Created at
details: Review details
editing: Editing review
shipment:
ship: Ship
created_at: Created at
id: ID
index_header: Shipments
inventory_state: Inventory state
manage: Manage shipments
method: Method
modified: Modified
name: Name
no_items: There are no items to display.
no_results: There are no shipments to display.
order: Order
show_header: Shipment details
sku: SKU
state: State
tracking_code: Tracking Code
update: Edit
update_header: Editing shipment
updated_at: Last update
channel:
create: Add channel
created_at: Created at
code: Code
currency_default: Default currency
enabled: Enabled
id: ID
index_header: Channels
locale_default: Default locale
manage: Manage channels
create_header: Add channel
name: Name
url: Url
no_results: There are no channels configured.
update: Edit
update_header: Editing channel
updated_at: Last update
header: Product reviews
product_review: Product review
no_results: There are no reviews for this product.
order_item:
product: Item
quantity: Qty

View file

@ -192,7 +192,7 @@
</td>
</tr>
{% endif %}
{% if product.averageRating is not null and product.averageRating != 0 %}
{% if 0 != product.averageRating %}
<tr>
<td>{{ 'sylius.product.rating'|trans }}</td>
<td>{{ product.averageRating|round(2, 'floor') }}</td>

View file

@ -17,7 +17,7 @@
{{ form_errors(form) }}
<form action="{{ path('sylius_backend_product_review_create') }}" method="post" class="form-horizontal" {{ form_enctype(form) }} novalidate>nie zbywa
<form action="{{ path('sylius_backend_product_review_create') }}" method="post" class="form-horizontal" {{ form_enctype(form) }} novalidate>
{% include 'SyliusWebBundle:Backend/Review:_form.html.twig' %}
{{ create() }}
</form>

View file

@ -141,9 +141,9 @@
<div>
<h3>{{ 'sylius.review.header'|trans }}</h3>
{% render(url("sylius_partial_product_review_list", { 'slug': product.slug, limit: 3 })) %}
{% render(url('sylius_product_reviews', { 'productId': product.id, limit: 3 })) %}
<hr />
{% render(url("sylius_partial_product_review_form", { 'slug': product.slug })) %}
{% render(url('sylius_product_review_create', { 'productId': product.id })) %}
</div>
{% endblock %}
@ -153,5 +153,11 @@
'bundles/syliusreview/js/sylius-review.js'
%}
<script type="text/javascript" src="{{ asset_url }}"></script>
<script>
var createMessage = "{{ 'sylius.resource.create'|trans({'%resource%': 'sylius.review.product_review'|trans}, 'flashes') }}";
$(document).ready(function() {
initializeAjaxForm(createMessage);
});
</script>
{% endjavascripts %}
{% endblock %}

View file

@ -1,4 +1,4 @@
{{ form_start(form, { 'attr': { 'class': 'form-horizontal', 'id': 'reviewForm' }, 'action': path('sylius_review_product', { 'slug': app.request.attributes.get('slug') }) }) }}
{{ form_start(form, { 'attr': { 'class': 'form-horizontal', 'id': 'reviewForm' }, 'action': path('sylius_product_review', { 'productId': app.request.attributes.get('productId') }) }) }}
<div class="panel panel-default">
<div class="panel-heading">
@ -28,4 +28,4 @@
<div class="panel-footer">
<button type="submit" class="btn btn-primary">{{ 'sylius.form.review.submit'|trans }}</button>
</div>
{{ form_row(form._token) }}
{{ form_end(form, {'render_rest': false}) }}

View file

@ -65,7 +65,7 @@ class Product extends BaseProduct implements ProductInterface, ReviewableProduct
/**
* @var float
*/
protected $averageRating;
protected $averageRating = 0;
public function __construct()
{
@ -368,7 +368,7 @@ class Product extends BaseProduct implements ProductInterface, ReviewableProduct
}
/**
* @return Collection|ReviewInterface[]
* {@inheritdoc}
*/
public function getReviews()
{
@ -376,15 +376,7 @@ class Product extends BaseProduct implements ProductInterface, ReviewableProduct
}
/**
* @param Collection $reviews
*/
public function setReviews(Collection $reviews)
{
$this->reviews = $reviews;
}
/**
* @param ReviewInterface $review
* {@inheritdoc}
*/
public function addReview(ReviewInterface $review)
{
@ -392,7 +384,7 @@ class Product extends BaseProduct implements ProductInterface, ReviewableProduct
}
/**
* @param ReviewInterface $review
* {@inheritdoc}
*/
public function removeReview(ReviewInterface $review)
{
@ -400,7 +392,7 @@ class Product extends BaseProduct implements ProductInterface, ReviewableProduct
}
/**
* @param float $averageRating
* {@inheritdoc}
*/
public function setAverageRating($averageRating)
{
@ -408,7 +400,7 @@ class Product extends BaseProduct implements ProductInterface, ReviewableProduct
}
/**
* @return float
* {@inheritdoc}
*/
public function getAverageRating()
{

View file

@ -12,6 +12,7 @@
namespace spec\Sylius\Component\Core\Model;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Review\Model\Review;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
@ -25,6 +26,6 @@ class ProductReviewSpec extends ObjectBehavior
function it_extends_review()
{
$this->shouldHaveType('Sylius\Component\Review\Model\Review');
$this->shouldHaveType(Review::class);
}
}

View file

@ -1,15 +1,18 @@
language: php
sudo: false
php:
- 5.3
- 5.4
- 7.0
- 5.6
- 5.5
before_script: composer install --dev --prefer-source
before_install:
- echo "memory_limit=2048M" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- phpenv config-rm xdebug.ini || true
script: bin/phpspec run -fpretty --verbose
install:
- composer install --no-interaction --prefer-source
notifications:
email: "travis-ci@sylius.org"
irc: "irc.freenode.org#sylius-dev"
script:
- bin/phpspec run -fpretty --verbose

View file

@ -1,6 +1,6 @@
CHANGELOG
=========
### v0.16.0
### v0.17.0
* Initial dev release.

View file

@ -17,28 +17,25 @@ use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
class AverageRatingCalculator implements AverageRatingCalculatorInterface
class AverageRatingCalculator implements ReviewableRatingCalculatorInterface
{
/**
* {@inheritdoc}
*/
public function calculate(ReviewableInterface $reviewable)
{
if (0 === count($reviews = $reviewable->getReviews())) {
return 0;
}
$sum = 0.0;
$sum = 0;
$reviewsNumber = 0;
$reviews = $reviewable->getReviews();
foreach ($reviews as $review) {
if (ReviewInterface::STATUS_ACCEPTED === $review->getStatus()) {
++$reviewsNumber;
$sum = $sum + $review->getRating();
$sum += $review->getRating();
}
}
return $sum / $reviewsNumber;
return 0 !== $reviewsNumber ? $sum / $reviewsNumber : 0;
}
}

View file

@ -15,8 +15,9 @@ use Sylius\Component\Review\Model\ReviewableInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
interface AverageRatingCalculatorInterface
interface ReviewableRatingCalculatorInterface
{
/**
* @param ReviewableInterface $reviewable

View file

@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Component\Review\Factory;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class ReviewFactory implements ReviewFactoryInterface
{
/**
* @var FactoryInterface
*/
private $factory;
/**
* @var RepositoryInterface
*/
private $subjectRepository;
/**
* @param FactoryInterface $factory
* @param RepositoryInterface $subjectRepository
*/
public function __construct(FactoryInterface $factory, RepositoryInterface $subjectRepository)
{
$this->factory = $factory;
$this->subjectRepository = $subjectRepository;
}
/**
* {@inheritdoc}
*/
public function createNew()
{
return $this->factory->createNew();
}
/**
* {@inheritdoc}
*/
public function createForSubject($subjectId)
{
if (null === $subject = $this->subjectRepository->find($subjectId)) {
throw new \InvalidArgumentException(sprintf('Review subject with id "%s" does not exist.', $subjectId));
}
$review = $this->factory->createNew();
$review->setReviewSubject($subject);
return $review;
}
}

View file

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Component\Review\Factory;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Review\Model\ReviewableInterface;
use Sylius\Component\Review\Model\ReviewInterface;
/**
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
interface ReviewFactoryInterface extends FactoryInterface
{
/**
* @param ReviewableInterface $subjectId
*
* @return ReviewInterface
*/
public function createForSubject($subjectId);
}

View file

@ -1,4 +1,4 @@
Copyright (c) 2011-2015 Paweł Jędrzejewski
Copyright (c) 2011-2016 Paweł Jędrzejewski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -11,12 +11,17 @@
namespace Sylius\Component\Review\Model;
use Sylius\Component\Resource\Model\TimestampableTrait;
/**
* @author Daniel Richter <nexyz9@gmail.com>
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Grzegorz Sadowski <grzegorz.sadowski@lakion.com>
*/
class Review implements ReviewInterface
{
use TimestampableTrait;
/**
* @var int
*/
@ -52,19 +57,6 @@ class Review implements ReviewInterface
*/
protected $reviewSubject;
/**
* @var \DateTime
*/
protected $createdAt;
/**
* @var \DateTime
*/
protected $updatedAt;
/**
* Constructor
*/
public function __construct()
{
$this->createdAt = new \DateTime();
@ -173,36 +165,4 @@ class Review implements ReviewInterface
{
$this->reviewSubject = $reviewSubject;
}
/**
* {@inheritdoc}
*/
public function setCreatedAt(\DateTime $createdAt)
{
$this->createdAt = $createdAt;
}
/**
* {@inheritdoc}
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* {@inheritdoc}
*/
public function setUpdatedAt(\DateTime $updatedAt)
{
$this->updatedAt = $updatedAt;
}
/**
* {@inheritdoc}
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
}

View file

@ -21,8 +21,6 @@ use Sylius\Component\Resource\Model\TimestampableInterface;
*/
interface ReviewInterface extends TimestampableInterface, ResourceInterface
{
const REVIEW_STATE_MACHINE_GRAPH = 'sylius_review';
const STATUS_NEW = 'new';
const STATUS_ACCEPTED = 'accepted';
const STATUS_REJECTED = 'rejected';

View file

@ -11,8 +11,6 @@
namespace Sylius\Component\Review\Model;
use Doctrine\Common\Collections\Collection;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
@ -24,7 +22,7 @@ interface ReviewableInterface
public function getName();
/**
* @return Collection|ReviewInterface[]
* @return ReviewInterface[]
*/
public function getReviews();

View file

@ -11,10 +11,12 @@
namespace Sylius\Component\Review\Model;
use Sylius\Component\Resource\Model\ResourceInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
*/
interface ReviewerInterface
interface ReviewerInterface extends ResourceInterface
{
/**
* @return string

View file

@ -1,4 +1,4 @@
Review Component [![Build status...](https://secure.travis-ci.org/Sylius/Review.png?branch=master)](http://travis-ci.org/Sylius/Report)
Review Component [![Build status...](https://secure.travis-ci.org/Sylius/Review.png?branch=master)](http://travis-ci.org/Sylius/Review)
=================
@ -10,7 +10,7 @@ Modern ecommerce for PHP and Symfony2. Visit [Sylius.org](http://sylius.org).
Documentation
-------------
Documentation is available on [**docs.sylius.org**](http://docs.sylius.org/en/latest/components/Report/index.html).
Documentation is available on [**docs.sylius.org**](http://docs.sylius.org/en/latest/components/Review/index.html).
Contributing
------------
@ -37,10 +37,10 @@ If you find a bug, please refer to the [Reporting a Bug](http://docs.sylius.org/
MIT License
-----------
License can be found [here](https://github.com/Sylius/Report/blob/master/LICENSE).
License can be found [here](https://github.com/Sylius/Review/blob/master/LICENSE).
Authors
-------
The component was originally created by [Paweł Jędrzejewski](http://pjedrzejewski.com).
See the list of [contributors](https://github.com/Sylius/Report/contributors).
The component was originally created by [Mateusz Zalewski](mateusz.zalewski@lakion.com) and [Grzegorz Sadowski](grzegorz.sadowski@lakion.com), based on [Daniel Richter](nexyz9@gmail.com) work.
See the list of [contributors](https://github.com/Sylius/Review/contributors).

View file

@ -14,6 +14,10 @@
"name": "Mateusz Zalewski",
"email": "mateusz.zalewski@lakion.com"
},
{
"name": "Grzegorz Sadowski",
"email": "grzegorz.sadowski@lakion.com"
},
{
"name": "Daniel Richter",
"email": "nexyz9@gmail.com"
@ -28,10 +32,10 @@
}
],
"require": {
"php": ">=5.5.9"
"php": "^5.5.9|^7.0"
},
"require-dev": {
"phpspec/phpspec": "~2.1"
"phpspec/phpspec": "^2.4"
},
"autoload": {
"psr-4": { "Sylius\\Component\\Review\\": "" }
@ -39,9 +43,10 @@
"autoload-dev": {
"psr-4": { "Sylius\\Component\\Review\\spec\\": "spec/" }
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "0.16-dev"
"dev-master": "0.17-dev"
}
}
}

Some files were not shown because too many files have changed in this diff Show more