codeliner / php-ddd-cargo-sample

PHP 7 Version of the cargo sample used in Eric Evans DDD book
http://codeliner.github.io/php-ddd-cargo-sample/
BSD 3-Clause "New" or "Revised" License
796 stars 151 forks source link

Running sample on PHP 5.6 #23

Closed josecelano closed 8 years ago

josecelano commented 8 years ago

Hi @codeliner . First at all, I have to say it's a very cool sample. I would like to play with it but I would like to run it with other samples in the sample vagrant machine with PHP 5.6. I have removed composer.lock and changed the composer.json to require only PHP 5.6. I removed the return type for these methods TrackingIdDoctrineType::convertToPHPValue and DoctrineEntityManagerFactory::__invoke, and it seems to work, or at least there is no other parser error. But I have a problem with JS:

This is the index response:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>PHP Cargo Shipping Application</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="/css/bootstrap.min.css">
        <link rel="stylesheet" href="/css/bootstrap-theme.min.css">
        <link rel="stylesheet" href="/css/style.css">
        <script language="javascript" type="text/javascript" src="/js/jquery.min.js"></script>
        <script language="javascript" type="text/javascript" src="/js/jquery.addons.js"></script>
        <script language="javascript" type="text/javascript" src="/js/bootstrap.min.js"></script>
        <script language="javascript" type="text/javascript" src="/js/notify.min.js"></script>
        <script language="javascript" type="text/javascript" src="/js/lodash.js"></script>
        <script language="javascript" type="text/javascript" src="/js/q.js"></script>
        <script language="javascript" type="text/javascript" src="/js/moment.min.js"></script>
        <script language="javascript" type="text/javascript" src="/js/riot.min.js"></script>
        <script language="javascript" type="text/javascript" src="/js/prooph.riot.app.js"></script>
        <script language="javascript" type="text/javascript" src="/js/stores.js"></script>
    </head>
    <body>
        <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
            <div class="container">
                <div class="navbar-header">                    
                    <a class="navbar-brand" href="/">PHP Cargo Shipping System</a>
                </div>
                <div class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <li><a href="#booking">Booking App</a></li>
                    </ul>
                </div><!--/.nav-collapse -->
            </div>
        </nav>
        <div class="container">
            <main></main>
            <hr>
            <footer>
                <p>&copy; 2013 - 2015 by Alexander Miertsch. All rights reserved.</p>
            </footer>
        </div> <!-- /container -->
        <script type="text/javascript">
            riot.tag("assign-route", "<div if=\"{ !ready }\">
    <p class=\"alert alert-info\">Fetching data from server ...</p>
</div>
<div if=\"{ ready }\">
    <div class=\"row\">
        <div class=\"col-md-12\">
            <h2>Cargo from {getLocation(cargo.get('origin')).get('name')} to {getLocation(cargo.get('final_destination')).get('name')}<span class=\"subheading\">TrackingId: { cargo.getId() }</span></h2>
        </div>
    </div>
    <div class=\"row\">
        <div class=\"col-md-12\">
            <h3>Available routes</h3>
        </div>
    </div>
    <div id=\"route-candidate-list\" >
        <route-candidate each=\"{ routeCandidates }\"
                         routecandidate=\"{ this }\"
                         trigger_event=\"{ parent.triggerEvent }\"
                         route_to=\"{ parent.routeTo }\"
                         get_location=\"{ parent.getLocation }\"></route-candidate>
    </div>
</div>
", function (context) {
        var self = this,
            _checkReady = function() {
                if (self.locations.length === 0) {
                    return;
                }

                if (! self.cargo) {
                    return;
                }

                if (! self.routeCandidates) {
                    return;
                }

                self.ready = true;
            };

        self.trackingId = context.trackingId;
        self.locations = [];
        self.routeCandidates = null;
        self.cargo = null;
        self.ready = false;

        self.getLocation = function(unLocode) {
            return _.find(self.locations, function(location) {
                return location.getId() === unLocode;
            });
        }

        self.triggerEvent = function(eventName, eventData) {
            context.app.trigger(eventName, eventData);
        }

        self.routeTo = function(routePath) {
            context.app.routeTo(context.router.get(routePath));
        }

        self.onLocationsRefreshed = function(locations) {
            self.locations = locations;
            _checkReady();
            self.update();
        }

        self.onCargoRefreshed = function(cargo) {
            if (cargo.getId() === self.trackingId) {
                self.cargo = cargo;

                context.app.trigger('load_cargo_routecandidates', cargo);
            }
        }

        self.onCargoRoutecandidatesLoaded = function(routeCandidates) {
            if (_.isEmpty(routeCandidates)) {
                return;
            }

            var first = _.first(routeCandidates);
            if (first.getParentId() !== self.trackingId) {
                return;
            }

            self.routeCandidates = routeCandidates;
            _checkReady();
            self.update();
        }

        self.on("mount", function() {
            context.app.on("locations_refreshed", self.onLocationsRefreshed);
            context.app.on("cargo_refreshed", self.onCargoRefreshed);
            context.app.on("cargo_routecandidates_loaded", self.onCargoRoutecandidatesLoaded);

            context.app.trigger("refresh_locations");
            context.app.trigger("refresh_cargo", new Resource(self.trackingId, 'cargo', {}));
        });

        self.on("unmount", function() {
            context.app.off("locations_refreshed", self.onLocationsRefreshed);
            context.app.off("cargo_refreshed", self.onCargoRefreshed);
            context.app.off("cargo_routecandidates_loaded", self.onCargoRoutecandidatesLoaded);
        });
    });riot.tag("cargo-list", "<div if=\"{ !ready }\">
    <img src=\"/img/ajax-loader.gif\" alt=\"loading ...\" />
</div>
<div if=\"{ ready }\">
    <div class=\"row\">
        <div class=\"col-md-12\">
            <p if=\"{ !cargos.length }\" class=\"alert alert-danger\">The list is empty. How about booking the first <a href=\"{ router.get('cargos/add') }\">cargo</a>?</p>
            <ul if=\"{ cargos.length }\" id=\"cargo-list\">
                <cargo-list-entry each=\"{ cargos }\" cargo=\"{ this }\" get_location=\"{ parent.getLocation }\" router=\"{ parent.router }\"></cargo-list-entry>
            </ul>
        </div>
    </div>
    <div class=\"row\">
        <div class=\"col-md-12\">
            <p><a id=\"book-cargo\" class=\"btn btn-success btn-lg\" href=\"{ router.get('cargos/add') }\">Book new Cargo &raquo;</a></p>
        </div>
    </div>
</div>
", function (context) {
        var self = this,
            _checkReady = function() {
                if (! self.cargos) {
                    return;
                }

                if (! self.locations) {
                    return;
                }

                self.ready = true;
                self.update();
            };

        self.ready = false;
        self.router = context.router;

        self.getLocation = function(unLocode) {
            return _.find(self.locations, function(location) {
                return location.getId() === unLocode;
            })
        }

        self.onCargoListRefreshed = function(cargos) {
            self.cargos = cargos;
            _checkReady();
            self.update();
        }

        self.onLocationsRefreshed = function(locations) {
            self.locations = locations;
            _checkReady();
            self.update();
        }

        self.on('mount', function() {
            context.app.on('cargo_list_refreshed', self.onCargoListRefreshed);
            context.app.on('locations_refreshed', self.onLocationsRefreshed);

            context.app.trigger('refresh_cargo_list');
            context.app.trigger('refresh_locations');
        })

        self.on("unmount", function() {
            context.app.off('cargo_list_refreshed', self.onCargoListRefreshed);
            context.app.off('locations_refreshed', self.onLocationsRefreshed);
        })
    });riot.tag("cargo-list-entry", "<li>
    <a href=\"{ router.get('cargos/' + cargo.getId()) }\" class=\"cargo-details-link\">{ cargo.getId() }</a> From: { getLocation(cargo.get('origin')).get('name') } To: { getLocation(cargo.get('final_destination')).get('name') }
</li>
", function (context) {
        var self = this;

        self.cargo = context.cargo;
        self.getLocation = context.get_location;
        console.log(self.getLocation(self.cargo.get('origin')));
        self.router = context.router;
    });riot.tag("jb-assign-route", "<h1>Assign Route to Cargo</h1>
", function (context) {

    });riot.tag("jb-cargo-list", "<h1>Cargo Booking Application</h1>
<p>Choose a Cargo from the list to view it's routing details or book a new Cargo.</p>
", function (context) {

    });riot.tag("jb-new-cargo", "<h1>Book a new Cargo</h1>
<p>In the ChapterFour version of the Shipping System you can specify origin and destination of a Cargo. In a second step, the booking system suggest possible routes and you can assign the new Cargo to one of the routes.</p>
", function (context) {

    });riot.tag("jb-show-cargo", "<h1>Cargo Overview</h1>
", function (context) {

    });riot.tag("new-cargo", "<div class=\"row\">
    <div class=\"col-md-12\">
        <form id=\"new-cargo-form\">
            <div class=\"form-group\">
                <label>Origin</label>
                <select name=\"origin\" class=\"form-control\" onchange=\"{ onChangeOrigin }\">
                    <option each=\"{ locations }\" value=\"{ unLocode }\">{ name }</option>
                </select>
            </div>
            <div class=\"{ form-group: true, has-error: hasError }\">
                <label>Destination</label>
                <select name=\"final_destination\" class=\"form-control\" onchange=\"{ onChangeDestination }\">
                    <option each=\"{ locations }\" value=\"{ unLocode }\">{ name }</option>
                </select>
                <span if=\"{ hasError }\" class=\"help-block\">Origin and Destination must not be the same location!</span>
            </div>
            <div class=\"form-group\">
                <input type=\"submit\" name=\"save\" value=\"assign to itinerary\" class=\"{ btn: true, btn-success: true, disabled: !isValid }\" onclick=\"{ onSave }\">
            </div>
        </form>
    </div>
</div>
", function (context) {
        var self = this,
            _checkIsValid = function () {
                self.hasError = false;
                self.isValid = false;

                if (!_.isEmpty(self.origin.value) && !_.isEmpty(self.final_destination.value)) {
                    if (self.origin.value === self.final_destination.value) {
                        self.hasError = true;
                        self.isValid = false;
                        return;
                    }

                    self.isValid = true;
                }
            };

        self.locations = [];

        self.hasError = false;
        self.isValid = false;

        self.locations.push({
            unLocode : '',
            name : 'Fetching locations from server ...'
        });

        self.onChangeOrigin = function (e) {
            _checkIsValid();
        }

        self.onChangeDestination = function (e) {
            _checkIsValid();
        }

        self.onSave = function (e) {
            e.preventDefault();

            _checkIsValid();

            if (! self.isValid) {
                return;
            }

            context.app.one('cargo_created', function(cargo) {
                window.location.hash = context.router.get('cargos/' + cargo.getId() + '/assign_route');
            });

            context.app.trigger('create_cargo', {origin: self.origin.value, destination: self.final_destination.value});
        }

        self.on('mount', function () {
            context.app.one("locations_refreshed", function(locations) {
                self.locations = [];

                self.locations.push({
                    unLocode : '',
                    name : 'Select a location'
                })

                _.forEach(locations, function (location) {
                    self.locations.push(location.getProps())
                });

                self.update();
            });

            context.app.trigger("refresh_locations");
        });
    });riot.tag("route-candidate-leg", "<div class=\"row highlight\">
    <div class=\"col-md-12\">
        <strong>{context.get_location(leg.load_location).get('name')}</strong>&nbsp;{print_time(leg.load_time)}&nbsp;<i class=\"glyphicon glyphicon-arrow-right\"></i> <strong>{context.get_location(leg.unload_location).get('name')}</strong>&nbsp;{print_time(leg.unload_time)}
    </div>
</div>
", function (context) {
        var self = this;
        self.leg = context.leg;
        self.context = context;
        self.print_time = function(timeStr) {
            return moment(timeStr).format('YYYY/MM/DD HH:mm');
        }
    });riot.tag("route-candidate", "<div class=\"row\">
    <div class=\"col-md-12\">
        <div class=\"panel panel-default itinerary\">
            <div class=\"panel-body\">
                <route-candidate-leg each=\"{ routeCandidate.get('legs') }\" leg=\"{ this }\" get_location=\"{ parent.context.get_location }\"></route-candidate-leg>
                <p>&nbsp;</p>
                <div class=\"row\">
                    <div class=\"col-md-12\">
                        <a href=\"#\"
                           class=\"btn btn-success assign-cargo-btn\" onclick=\"{ onAssignCargo }\">Assign to Cargo</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
", function (context) {
        var self = this;
        self.context = context;
        self.routeCandidate = context.routecandidate;

        self.onAssignCargo = function(e) {
            e.preventDefault();

            context.trigger_event('assign_route_candidate_to_cargo', self.routeCandidate);

            context.route_to('');
        }
    });riot.tag("show-cargo", "<div if=\"{ !ready }\">
    <img src=\"/img/ajax-loader.gif\" alt=\"loading ...\" />
</div>
<div if=\"{ ready }\">
    <div class=\"row\">
        <div class=\"col-md-12\">
            <h2>Cargo from { getLocation(cargo.get('origin')).get('name') } ({ cargo.getId() })</h2>
        </div>
    </div>
    <div class=\"row\">
        <div class=\"col-md-12\">
            <h3>route specification</h3>
        </div>
    </div>
    <div class=\"row\">
        <div class=\"col-md-6 alert-info\">
            <strong>From: </strong>{ getLocation(cargo.get('origin')).get('name') }
        </div>
        <div class=\"col-md-6 alert-info\">
            <strong>To: </strong>{ getLocation(cargo.get('final_destination')).get('name') }
        </div>
    </div>
    <div class=\"row\">
        <div class=\"col-md-12\">
            <h3>Itinerary</h3>
        </div>
    </div>
    <div class=\"row\">
        <div class=\"col-md-12\">
            <div class=\"panel panel-default itinerary\">
                <div class=\"panel-body\">
                    <p class=\"alert alert-warning\" if=\"{ cargo.get('legs').length == 0 }\"><i class=\"glyphicon glyphicon-warning-sign\"></i>&nbsp;Cargo is not assigned to a route! <a href=\"{ router.get('cargos/' + cargo.getId() + '/assign_route') }\" class=\"btn btn-primary\">assign now</a></p>
                    <div if=\"{ cargo.get('legs').length > 0 }\">
                        <route-candidate-leg each=\"{ cargo.get('legs') }\" leg=\"{ this }\" get_location=\"{ parent.getLocation }\"></route-candidate-leg>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
", function (context) {
        var self = this,
            _checkReady = function() {
                if (! self.cargo) {
                    return;
                }

                self.ready = true;
            };

        self.ready = false;

        self.trackingId = context.trackingId;
        self.router = context.router;

        self.getLocation = function(unLocode) {
            return _.find(self.locations, function(location) {
                return location.getId() === unLocode;
            })
        }

        self.onCargoRefreshed = function(cargo) {
            if (! cargo.getId() === self.trackingId) {
                return;
            }

            self.cargo = cargo;

            _checkReady();
            self.update();
        }

        self.onLocationsRefreshed = function(locations) {
            self.locations = locations;
            _checkReady();
            self.update();
        }

        self.on("mount", function() {
            context.app.on('cargo_refreshed', self.onCargoRefreshed);
            context.app.on('locations_refreshed', self.onLocationsRefreshed);

            context.app.trigger('refresh_cargo', new Resource(self.trackingId, 'cargo', {}));
            context.app.trigger('refresh_locations');
        });

        self.on("unmount", function() {
            context.app.off('cargo_refreshed', self.onCargoRefreshed);
            context.app.off('locations_refreshed', self.onLocationsRefreshed);
        })
    });riot.tag("booking-app", "<div class=\"jumbotron\">
    <jb-cargo-list if=\"{ activeMode == 'cargo-list' }\"></jb-cargo-list>
    <jb-show-cargo if=\"{ activeMode == 'show-cargo' }\"></jb-show-cargo>
    <jb-new-cargo if=\"{ activeMode == 'new-cargo' }\"></jb-new-cargo>
    <jb-assign-route if=\"{ activeMode == 'assign-route' }\"></jb-assign-route>
</div>
<div id=\"booking-app-container\">
    <img src=\"/img/ajax-loader.gif\" alt=\"loading ...\" />
</div>
<footer>
    <div class=\"row\">
        <div class=\"col-md-12\">
            <a href=\"{ router.get('') }\" class=\"btn btn-default icon icon-align-left icon-arrow-left pull-right\">back to overview</a>
        </div>
    </div>
</footer>
", function (context) {
        var self = this,
            renderCargoList = function (mixins) {
                context.app.renderInto(self, "#booking-app-container", "cargo-list", mixins);
                self.activeMode = "cargo-list";
                self.update();
            },
            renderNewCargo = function (mixins) {
                context.app.renderInto(self, "#booking-app-container", "new-cargo", mixins);
                self.activeMode = "new-cargo";
                self.update();
            },
            renderShowCargo = function (mixins) {
                context.app.renderInto(self, "#booking-app-container", "show-cargo", mixins);
                self.activeMode = "show-cargo";
                self.update();
            },
            renderAssignRoute = function (mixins) {
                context.app.renderInto(self, "#booking-app-container", "assign-route", mixins);
                self.activeMode = "assign-route";
                self.update();
            },
            _routeListener = function (con) {
                var rm = con.routeMatch;
                if (! rm[0]) {
                    renderCargoList({router : con.router});
                    return;
                }

                if (rm[0] == "cargos") {
                    if (! rm[1]) {
                        renderCargoList({router : con.router});
                        return;
                    }

                    if (rm[1] == "add") {
                        renderNewCargo({router : con.router});
                        return;
                    }

                    if (! rm[2]) {
                        renderShowCargo({router : con.router, trackingId : rm[1]});
                    }

                    if (rm[2] == "assign_route") {
                        renderAssignRoute({router : con.router, trackingId : rm[1]})
                    }
                }
            };

        self.router = context.router;

        self.on("mount", function () {
            context.router.on("route", _routeListener);
        })
    });riot.tag("home", "<div class=\"jumbotron\">
    <h1>Welcome to the PHP DDD Cargo Sample</h1>
    <p><strong>Congratulations!</strong> You have successfully installed the Container Shipping System. You are currently running the <strong>{chapter}</strong> Version of the Application.</p>
    <p><a class=\"btn btn-success btn-lg\" href=\"https://github.com/prooph/php-ddd-cargo-sample\" target=\"_blank\">Fork PHP DDD Cargo Sample on GitHub &raquo;</a></p>
</div>
<div class=\"row\">
    <div class=\"col-md-12\">
        <div class=\"panel panel-default\">
            <div class=\"panel-heading\">
                <h3 class=\"panel-title\">Goal of the Sample Application</h3>
            </div>
            <div class=\"panel-body\">
                <p>The Cargo Shipping System is based on the example used in Eric Evans book Domain-Driven Design: <i>Tackling Complexity in the Heart of Software</i>. It\'s a PHP Version of the Cargo Sample to show you how DDD can be applied to a PHP Project.</p>
            </div>
        </div>
    </div>
</div>
", function (context) {
        var self = this;

        self.chapter = context.app.config.chapter;
    });riot.tag("main", "<div class=\"content\">

</div>
", function(context) {
        var self = this,
                _routeListener = function (con) {
                    var rm = con.routeMatch;

                    if (!rm[0]) {
                        con.app.renderInto(self, ".content", "home");
                        con.app.removeRouterForRoutePath(["booking"]);
                        return;
                    }

                    if (rm[0] == "booking") {
                        var subRouter = con.router.getSubRouter("booking");
                        con.app.renderInto(self, ".content", "booking-app", {router : subRouter});
                        subRouter.route(rm, con);
                        return;
                    }
                };

        context.router.on("route", _routeListener);
    });
            var CargoUI = ProophRiot.App.create({
                config : {
                    url : {
                        apiRoot: '/api/',
                        forResource : function (resource) {
                            return this.apiRoot + this.resourcePlural(resource.getName()) + '/' + resource.getId();
                        },
                        forResourceCollection : function (resourceName) {
                            return this.apiRoot + this.resourcePlural(resourceName);
                        },
                        resourcePlural: function(resourceName) {
                            return resourceName + 's';
                        },
                        forChildResource : function(childResource) {
                            return this.apiRoot + this.resourcePlural(childResource.getParentName())
                                    + '/' + childResource.getParentId()
                                    + '/' + this.resourcePlural(childResource.getName())
                                    + '/' + childResource.getId()
                        },
                        forChildResourceCollection : function(parentResource, childResourceName) {
                            return this.apiRoot + this.resourcePlural(parentResource.getName())
                                    + '/' + parentResource.getId()
                                    + '/' + this.resourcePlural(childResourceName);
                        }
                    },
                    chapter: "ChapterFour"
                }
            });

            var locationsStore = new LocationStore(CargoUI);
            var cargoStore = new CargoStore(CargoUI);
            var routeCandidateStore = new RouteCandidateStore(CargoUI);

            $(function () {
                CargoUI.bootstrap("main").ready();
            });
        </script>
    </body>
</html>

and there is an error in line 42: Uncaught SyntaxError: Unexpected token ILLEGAL'. It seems methodRiotCompiler::compileFileis not working well, may be becuase functionstr_replace` has a different behavior on PHP 7. Is possible to run the sample on PHP 5.6 or are there a lot of errors like this one?

codeliner commented 8 years ago

Hi @josecelano, the JS problem seems to be a bug. TBH, the riot compiler is not tested very well. Normally you would need to setup node.js and grunt to compile riot.js but I wanted to provide a simple PHP solution so that you don't need to setup additional tools just to try the sample. On which system are you trying to run the sample? I'm asking because the behat feature tests run against the frontend and they work on my machine and travis-ci. However, I'll have an idea what causes the problem and will check it later. Regarding PHP 5.6 you would need to change the entire domain model (CargoBackend) not only the Doctrine part. If you want to run the sample on PHP 5.6 you can checkout this release: https://github.com/codeliner/php-ddd-cargo-sample/releases/tag/ChapterFour It is the last version of the sample before the rewrite. But it uses Zend Framework 2 as application framework and Backbone.js in the frontend. Also Doctrine 2.5 features like Embeddables are not implemented in this version.

josecelano commented 8 years ago

Hi @codeliner, I think the problem is the riot compiler is not removing line breaks. Regarding your question I am using the laravel homestead vagrant box.

Then I think I am going to use the homestead version for PHP 7

Off topic:

I have been looking for a way to integrate a PHP framework (first Symfony and now Laravel) with a frontend library/framework without duplicating templates and doing too much "cool" unstable stuff. I have only found this presentation:

https://speakerdeck.com/bastianhofmann/bring-your-php-application-to-the-next-level-with-react-dot-js

from @bashofmann, but I had not found any sample until now. This project is the first real implemented project I have seen. Mainly I am a backend developer, I have been using JQuery when I need to implement more dinamic pages but It becomes a mess in long term. I am going to start a new project from scratch and I want to implement something like this sample or PHP+REACTJS. Initially I thougth to use REACTJS but I think RIOTJS is cleaner than REACTJS.

codeliner commented 8 years ago

@josecelano That was also my idea but line endings are replaced here: https://github.com/codeliner/php-ddd-cargo-sample/blob/master/CargoUI/src/RiotCompiler.php#L27

and here: https://github.com/codeliner/php-ddd-cargo-sample/blob/master/CargoUI/src/RiotCompiler.php#L61

Problem is, on my machine it works. Can you check the compiler on your system? If you find a solution a PR would be nice :). Unfortunately, I don't have much time atm otherwise I'd install homestead and try it myself :(

Regarding JS integration: What do you want to achieve? Do you want to reuse templates on server and client? If so your only chance is using mustache. You'll find PHP as well as js mustache engines. I've done this in the past and it is nice but mustache is logic less so templates are a bit complex.

The cargo sample is just split into a backend module with a REST API and a frontend module using riot js and this small (buggy :) riot compiler). I prefer riot over react because I don't like react's JSX syntax. Jumping between PHP and JS is enough. Don't want to learn that meta language which also requires a compiler etc. Riot js is super simple but building complex UIs is not that easy. It is recommended to set up a flux architecture. The cargo sample does this with a lightweight custom solution.

Another set up I really like is the one you can find in this sample app: https://github.com/prooph/proophessor-do

There we use small riot.js apps on every page (so no Single Page App and no flux architecture) but each page is served and rendered first with PHP. A special View Helper then renders riot tags dynamically into the layout. The cool thing is you can use PHP in your riot tags for example to inject links like you can see here: https://github.com/prooph/proophessor-do/blob/master/view/riot/user-form.phtml#L47

The riot view helper looks much the same like the one in the cargo sample: https://github.com/prooph/proophessor-do/blob/master/src/App/View/Helper/RiotTag.php

Cheers

josecelano commented 8 years ago

I fixed the problem using:

        $tagHtml = str_replace('"', "\"", $tagHtml);
        $tagHtml = preg_replace( "/\r|\n/", "", $tagHtml);

instead of:

        return 'riot.tag("'.$tagName.'", "' . str_replace($this->search, $this->replace, $tagHtml) . '", '.$jsFunc.');';

In RiotCompiler::class.

But now I have more problems regarding PHP 7 exclusive reserved words as 'declare' or funtions type hinting.

codeliner commented 8 years ago

@josecelano Do you run homestead on a windows host? And does your git replace \n with \r\n when checking out a repo from github?

However, I'll add your fix. Thank you for reporting and finding the bug.

Do you run the cargo sample on PHP 7 now? Or do you mean you have more problems because you are still trying to run the sample with PHP 5.6? The new version of the cargo sample only supports PHP 7 as I said before.

codeliner commented 8 years ago

@josecelano Added your fix: https://github.com/codeliner/php-ddd-cargo-sample/commit/a194a2f6b732aed69e22a76198d6fe9e361f814f

Can you verify and close the issue if problem is solved?

josecelano commented 8 years ago

Thanks @codeliner. It works.

Yes, I run homestead on a Windows 8.1 host and I replacing \n with \r\n becuase my IDE is running on Windows and I have autocrlf = trueoption in .gitcofig.

I have more problems because I still trying to run the sample on PHP 5.6. If I have time I wuold like to port the sample to PHP 5.6.

josecelano commented 8 years ago

I have removed PHP 7 features in my fork:

https://github.com/josecelano/php-ddd-cargo-sample

It works on PHP 5.6.15.

codeliner commented 8 years ago

@josecelano I've added a note in the readme https://github.com/codeliner/php-ddd-cargo-sample/blob/master/README.md#php-56-compatible-version if other people are also looking for a 5.6 version.