The Elm way of transforming flags to model - elm

I have the following types in my app:
type Page
= Welcome
| Cards
type alias Flags =
{ recipientName : String
, products : List Product
}
type alias Product =
{ id : Int
, name : String
, price : Float
, liked : Maybe Bool
}
type alias Model =
{ recipientName : String
, currentPage : Page
, products : List Product
}
I am passing an array of products as flags. Here's what my init looks like:
init : Flags -> ( Model, Cmd Msg )
init flags =
let
{ recipientName, products } =
flags
in
Model recipientName Welcome products
|> withNoCmd
The challenge I'm facing is that the products in this array only have id, name, and price attributes. So, given the Flags definition, every time I extend Product with a new attribute (such as liked), the array of products passed as flags will need to have that attribute as well. For now, I just render them as empty, but this doesn't feel right, so I was wondering what is the Elm way™ of receiving flags and transforming them into the model? Thank you!

It sounds like your Product is already defined as an input (or the environment) of your app:
type alias Product =
{ id : Int
, name : String
, price : Float
}
and you are augmenting this with info that relates the Recipient to the Products. I'd suggest splitting this out into its own type that can grow as your app grows, eg:
type alias Opinion =
{ liked : Maybe Bool
, review : String
, preferredColor : Color
}
then you can tie these together in your Model:
type alias Model =
{ recipientName : String
, currentPage : Page
, products : List (Product, Opinion)
}
or, depending on how the application works, you might end up wanting to look up the recipient's opinion by product.id:
...
, products : List Product
, opinions : Dict Int Opinion
The point is that if you keep the original Product unchanged, you can build a small library of functions that work on Product, both for inventory (where no recipient is involved) and for the customer. Maybe you can re-use the Opinion type for customer metrics.
If these two types are likely to evolve, keeping them separate can help ensure you don't end up with messy and bug-attracting interdependencies.

Related

getProduct()->getTag() return null, when it should return tags associated to the Product

In my project, we have products that has tag called serviceItem. Those item with that tag when ordered should be separated by the quantity into individuals order.
It issue is that getTags() returns null, and getTagIds gets "Call to a member function getTagIds() on null" when it gets to the next loop.
Is there a reason for why getTags() returns null?
private function transformOrderLines(OrderEntity $order): array
{
/**
* TODO: If we need to send advanced prices,
* the price value of the the lines array should be changed to caldulate the advanced price,
* with the built in quantity calculator
*/
$lines = [];
foreach ($order->getLineItems() as $orderLine) {
$hasDsmServiceItemTag = $orderLine->getProduct()->getTags();
$lines[] = [
'name' => $orderLine->getLabel(),
'sku' => substr($orderLine->getProduct()->getProductNumber(), 0, 19),
'price' => (string) ($orderLine->getProduct()->getPrice()->first()->getNet()
* $order->getCurrencyFactor()), //gets original price, calculates factor
'quantity' => (string) $orderLine->getQuantity()
];
}
$shipping = $this->transformShipping($order);
if ($shipping) {
$lines = array_merge($lines, $shipping);
}
return $lines;
}`
I also tried $orderLine->getProduct()->getTags()->getName() it also return "Call to a member function getTags() on null"
The problem is wherever the $order is fetched from the DB the orderLineItem.product.tag association is not included in the criteria.
For performance reasons shopware does not lazily load all association when you access them on entities, but you have to exactly define which associations should be included when you fetch the entities from the database.
For the full explanation take a look at the docs.

Create value with prefix and auto increment in migration using laravel

I want to create a database table in laravel using migration, I have 4 columns in that table
1) ID(Auto-Increment, Primary Key)
2) Book Name
2) Book ID
4) Price
Now, I need to automatically fill value of BOOK ID column, value like this
Book ID = 'Book_1' (here "Book_" is prefix & 1 is value from ID column)
so for auto increment we create like this
$table->increments('id');
I need for BookID, how to write for that.
possible solution.
NOTE : not in table creation (migration) but when actually storing data.
create an ordinary varchar column to store name.
$table->string('name');
in the AppServiceProvider class boot() function. do something like this.
let's imagine your particular model is 'Book'
Book::created(function ($book) {
$book->update(['name' => 'Book_' . $book->id]);
}
this will bind an event to the 'Book' creation. when every time new book is saved to the database, name will automatically generate and save.
If the BookID column is only for the representational purpose you can add an accessor.
public function getBookIdAttribute()
{
return 'Book_' . $this->attributes['id'];
}
else, you can accomplish by adding this to your model,
protected static function boot()
{
parent::boot();
static::created(function ($model) {
$model->BookID = 'Book_' . $model->id;
$model->save();
});
}

Modeling condition, nested forms with elm-simple-forms

I have a form that starts with a select. Depending on what was selected the form then expands to a common main bit and a details section that depends on the selection.
I started modeling with a separate details section
type ProductDetails
= Book Book.Model
| Brochure Brochure.Model
| Card Card.Model
type alias Model =
{ form : Form CustomError Estimate
, details : ProductDetails -- a Form CustomerError CardModel / BookModel / ....
, msg : String
}
but this is becoming quite convoluted to handle in e.g. view.
The alternative would seem to be conditionally to add the details into the main form model - e.g.
type alias Estimate =
{ customer : String
, project : String
, product : String
, details : ProductDetails
}
Before I get started I’d welcome experience from others on what has worked well
If I understand correctly, you have separate modules for Book, Brochure and Card? I don't quite understand what is the purpose of your Model but I would structure it like this:
import Book
import Brochure
import Card
type Products
= Book
| Brochure
| Card
type Msg
= Details Products
type alias Model =
{ selectedProduct : Product
}
update : Msg -> Model -> Model
update msg model =
case msg of
Details prd ->
Model prd
view : Model -> Html
view model =
model.selectedProduct.view
So as you can see, now you define all available products and then you say that Msg can be Details, which would show details, and it's function would be to set selectedProduct value in Model to selected Product. You can implement selecting with:
button [ onClick (Details Book) ] [ text "Book" ]
for example to select a book. Later on you want to make it dynamic and the first instinct should be to be able to call the view function of selected Product.
In other case you could define view which would require some fields, which every Product's model would contain and then you could use them to write some information on site.
Please note that code above isn't meant to work, it's just to represent the idea.
I'm not familiar with elm-simple-forms, but this seems like a good representation of your form:
type ProductType
= TBook
| TBrochure
| TCard
type Product
= Book Book.Model
| Brochure Brochure.Model
| Card Card.Model
type alias Model =
{ product : Maybe Product
, customer : String
, project : String
}
type Msg
= SelectProductType ProductType
init : Model
init =
{ product = Nothing
, customer = ""
, project = ""
}
update : Msg -> Model -> Model
update msg model =
case msg of
SelectProductType product ->
{model | product =
case product of
TBook -> Book Book.init
TBrochure -> Brochure Brochure.init
TCard -> Card Card.init
}
view : Model -> Html Msg
view model =
case model.product of
Nothing ->
myProductTypeSelect
Just product ->
withCommonFormInputs
<| case product of
Book submodel -> Book.view submodel
Brochure submodel -> Brochure.view submodel
Card submodel -> Card.view submodel
The Maybe gives you a nice way to choose between the first form (just the select) and the second form (customer details + selected product type details).
The Book.view etc. give you Html which you can add to the common case:
withCommonFormInputs : Model -> Html Msg -> Html Msg
withCommonFormInputs model productInputs =
div
[]
[ input [] [] -- customer
, input [] [] -- project
, productInputs -- product (Book, Brochure, Card) subform
]
I ended up using a Dict of the various fields and changed the fields when the product changed. Trying to model each product more explicitly create more boiler plate than I needed.

GORM Domain Mapping Issue

I've got a bit of a complicated domain model I'm trying to implement and I'm having some trouble. (On top of that, I'm quite new to all this!)
I have a User domain which has multiple roles and multiple tests. The Role domain works great. The Test domain is a bit more compilciated though because it requires two foreign keys instead of just 1 like in the Role domain. The first foreign key is the user_id and the second is a uni_id (university ID).
The User domain model contains the following
class User {
static hasMany = [roles:Role, tests:Test]
Integer userId
...
static mapping = {
table 'user_data'
id generator: 'assigned', name: 'userId', type: 'long'
userId column: 'user_id'
version false
roles joinTable:[name:'user_role', key:'user_id']
tests joinTable:[name:'user_test', key:'user_id'] // Here is where I run into trouble
}
static constraints = {
}
}
The Test domain contains
class Test {
static belongsTo = User
static hasMany = [users:User]
static hasOne = [uni:Uni]
Integer testId // primary key
String testType
static mapping = {
table 'test'
id generator: 'assigned', name: 'testId', type: 'long'
testId column: 'test_id'
users joinTable:[name:'user_test', key:'test_id']
uni joinTable:[name:'user_test', key:'test_id'] // If I leave this out, everything is groovy
version false
}
static constraints = {
}
}
and the Uni domain contains
class Uni {
static belongsTo = Test
static hasMany = [tests:Test]
Integer uniId // primary key
String shortName
String fullName
static mapping = {
table 'uni'
id generator: 'assigned', name: 'uniId', type: 'long'
uniId column: 'uni_id'
version false
tests joinTable:[name:'user_test', key:'uni_id']
}
static constraints = {
}
}
If its not clear, what I'm trying to do is pull in the University ID, Test ID, and User ID to a table user_test to find based on the User ID which tests they have taken. Is there a simple way to do this?
The kinds of errors I'm getting lead me to believe that for some reason it is trying to perform all actions on the table test instead of user_test. For example,
Unsuccessful: alter table test add uni_id int not null
I'd like to be able to access the test and university information corresonding to the specific user via user.tests.testType and user.tests.uni.fullName or something to that extent. What am I doing wrong? More importantly, is there a better way to do this?! Thanks in advance!
Edit 1: something interesting I just thought of.. a user can have multiple tests, but the inverse isn’t true. A given test will never be shared among multiple people. So I think that changes things a bit.. I'll do some reading and post if I come up with anything new.
Edit 2: Here's the Role domain
class Role {
static belongsTo = User
static hasMany = [users:User]
Integer roleId
String shortName
String roleName
Integer roleLevel
static mapping = {
table 'role'
id generator: 'assigned', name: 'roleId', type: 'long'
roleId column: 'role_id'
users joinTable:[name:'user_role', column:'user_id', key:'role_id']
version false
}
static constraints = {
}
}
Edit 3: I am now trying to store all test information in the Test domain model and simply choose the Uni name to store as a field in Test, but am getting weird errors when I try this. My new files look like this
class User {
static hasMany = [roles:Role, tests:Test]
Integer userId
static mapping = {
table 'user_data'
id generator: 'assigned', name: 'userId', type: 'long'
userId column: 'user_id'
version false
roles joinTable:[name:'user_role', key:'user_id']
}
static constraints = {
}
}
and
class Test {
static belongsTo = User
Integer testId // primary key
Integer testTypeId
String testTypeName
String testUni
Date testDate
static mapping = {
table 'test'
id generator: 'assigned', name: 'testId', type: 'long'
testId column: 'test_id'
version false
}
static constraints = {
}
}
but now I'm getting the following error when I try to run it Caused by: org.hibernate.MappingException: Missing type or column for column[tests_test] on domain[User] referencing[Test]
Any idea what that's about?
Ok, one issue you have is that you're trying to share the User-to-Test association join table with the Test-to-Unit association. That's not going to work.
Lets look at it in database terms. I'm not an ASCII art expert, so I hope this diagram doesn't make your eyes bleed.
user_data (userId) |---|< (user_id) user_test (test_id) >|---| (testId) test
The diagram above shows the database implementation of the many-to-many association between the User and Test domain classes. You can see that the user_data.userId links to user_test.user_id and user_test.test_id links to test.testId.
Now here's where it starts to get weird. There are two different associations between Test and Uni: a bidirectional one-to-one and a one-to-many. I just don't understand that. But I want to illustrate an important issue with your join tables, so here it is.
test (testId) |---|< (test_id) user_test (uni_id) >|---| (uniId) uni
Because you're using the same join table (user_test) for two different associations you're asking GORM to create a table like this:
USER_TEST
- USER_ID
- TEST_ID
- UNIT_ID
GORM won't do that because join tables are supposed to have only two fields. Not only that, but also you're defining a many-to-many in database terms, and yet a bidirectional one-to-one and a one-to-many in GORM terms. Ouch!
TODO
The first change I recommend is to use a different join table for the Test-Uni association.
Finally got everything working (after a bit of modification in terms of the domain model)
class User {
static hasMany = [roles:Role, tests:Test]
Integer userId
static mapping = {
table 'user_data'
id generator: 'assigned', name: 'userId', type: 'long'
userId column: 'user_id'
version false
roles joinTable:[name:'user_role', column:'role_id', key:'user_id']
}
static constraints = {
}
}
and
class Test {
User user
Integer testId // primary key
String testType
String testUni
Date testDate
static mapping = {
table 'test'
id generator: 'assigned', name: 'testId', type: 'long'
testId column: 'test_id'
version false
}
static constraints = {
}
}
with
class Uni {
Integer uniId // primary key
String shortName
String fullName
static mapping = {
table 'uni'
id generator: 'assigned', name: 'uniId', type: 'long'
uniId column: 'uni_id'
version false
}
static constraints = {
}
}
So now what I'm doing is selecting the university from a drop down tab in my GSP and just saving it in Test as the string testUni. Then, the big change was removing all joinTables between the three and adding User user to Test. I'm still a little fuzzy on why what I was doing before didn't work, but I won't complain about a working app!

JasperReports for grails: Using HashMap as Model?

Imagine I have two classes in Groovy that look like that:
class Person {
int id;
String name;
}
class Item {
int id;
int price;
}
Now it would be simple to create a JasperReport listing all persons' names using the following SQL:
SELECT name FROM Person
Also, it would be easy to pass a Model from which the list should be created:
def p = Person.withCriteria {
eq('name','SomeGuy')
}
chain(controller:'jasper',action:'index',model:[data:p],params:params)
But what I want to do is to use the following query:
SELECT name, (SELECT sum(i.price) FROM Item f WHERE f.id=p.id) as priceSum FROM Person p
And now this is the part where I don't know how to go on: How can I pass any Model to the jasperReport? I can't just use
def p = Person.withCriteria {
eq('name','SomeGuy')
}
because then, the priceSum attribute would be missing.
What I would like to do is something like this:
def l = ['name':'SomeGuy','priceSum':'5000']
chain(controller:'jasper',action:'index',model:[data:l],params:params)
But this doesn't work either, it gives me:
Message: Cannot get property 'name' on null object
Caused by: java.lang.NullPointerException: Cannot get property 'name' on null object
Is there anything simliar to this that would work?