1
0
mirror of https://github.com/ToxicCrack/PrintABrick.git synced 2025-05-16 20:30:09 -07:00

Merged branch enhance/improve-loader into dev

This commit is contained in:
David Hübner 2017-04-16 22:23:07 +02:00
commit 9eb99a32ea
124 changed files with 6247 additions and 1818 deletions

1
.gitignore vendored
View File

@ -18,4 +18,5 @@
/node_modules/ /node_modules/
/app/Resources/libs/semantic/dist /app/Resources/libs/semantic/dist
/web/resources/ /web/resources/
/web/media/
.php_cs.cache .php_cs.cache

View File

@ -29,7 +29,8 @@ For full requirements see Symfony 3.2 [docs](http://symfony.com/doc/3.2/referenc
#### Database #### Database
1. Set application parameters in *app/config/parameters.yml* 1. Set application parameters in *app/config/parameters.yml*
2. Generate empty database by running command `$ php bin/console doctrine:database:create` 2. Generate empty database by running command `$ php bin/console doctrine:database:create`
3. Load LDraw models into database by running commad `$ php bin/console app:load:ldraw [ldraw_dir_path]` 3. Create database tables by running command `$ bin/console doctrine:schema:create`
3. Load LDraw models into database by running commad `$ php bin/console app:load:models <ldraw_dir> [--all] [--file=FILE] [--update]`
4. Load Rebrickable data into database by running command `$ php bin/console app:load:rebrickable` 4. Load Rebrickable data into database by running command `$ php bin/console app:load:rebrickable`
5. Load relations between LDraw models and Rebrickable parts by running command `$ php bin/console app:load:relation` 5. Load relations between LDraw models and Rebrickable parts by running command `$ php bin/console app:load:relation`

View File

@ -20,6 +20,7 @@ class AppKernel extends Kernel
new Oneup\FlysystemBundle\OneupFlysystemBundle(), new Oneup\FlysystemBundle\OneupFlysystemBundle(),
new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(), new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),
new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(), new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(),
new Liip\ImagineBundle\LiipImagineBundle(),
]; ];
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,85 +1,277 @@
ModelViewer = function() { var ModelViewer = function($dom_element, model_url) {
var container; var $this = this;
var camera, cameraTarget, scene, renderer, controls, object; this.container = document.createElement('div');
var width, height; this.container.setAttribute('class','model-view');
this.dom_element = $dom_element;
$dom_element.append(this.container);
this.initScene = function($container) { this.camera = null;
width = parseFloat($container.width()); this.scene = null;
height = parseFloat($container.height()); this.renderer = null;
this.controls = null;
this.width = parseFloat($dom_element.width());
this.height = parseFloat($dom_element.height());
this.scale = 1;
this.wireframe = false;
this.rendering = false;
this.container.style.display = "none";
this.toggleButton = null;
this.background = 0xffffff;
this.model_url = model_url;
this.loaded = false;
camera = new THREE.PerspectiveCamera(45, width/height, 0.1, 1000); this.object = null;
camera.position.set(-2, 2, 0.8);
camera.lookAt(new THREE.Vector3(0, 3, 0));
scene = new THREE.Scene(); this.initHtml();
scene.fog = new THREE.FogExp2(0x000000, 0.001); this.initScene();
// var grid = new THREE.GridHelper( 30, 70 );
// // grid.position.set(30/70,-0.5,30/70);
// scene.add( grid );
// Lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
scene.add( new THREE.AmbientLight( 0xf0f0f0 )); // this.stats = new Stats();
scene.background = new THREE.Color( 0x000000 ); // this.stats.dom.style.position = 'absolute';
// this.container.append(this.stats.dom);
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( scene.fog.color );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( width, height );
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMap.enabled = true;
renderer.shadowMap.renderReverseSided = false;
$container.append(renderer.domElement); function renderLoop() {
requestAnimationFrame(renderLoop);
if($this.rendering) {
if(!$this.loaded) {
$this.loadStl($this.model_url);
$this.loaded = true;
}
controls = new THREE.OrbitControls( camera, renderer.domElement ); if($this.stats) {
controls.addEventListener( 'change', this.render ); // add this only if there is no animation loop (requestAnimationFrame) $this.stats.update();
// controls.enableDamping = true; }
// controls.dampingFactor = 0.25; $this.render();
controls.enableZoom = true; }
};
$this.resize(parseInt($this.dom_element.width()), parseInt($this.dom_element.height()));
}
renderLoop();
};
ModelViewer.prototype.initHtml = function () {
$this = this;
var buttons = document.createElement("div");
buttons.setAttribute("class", "modelviewer-buttons");
this.wireframeButton = $('<button/>', {
'class':'model',
'style':'display:none',
'html':'<i class="eye icon"/>Wireframe',
'click': $this.toggleMaterial.bind($this)
}).appendTo(buttons);
this.toggleButton = $('<button/>', {
'class':'toggle',
'html':'<i class="cube icon"/>3D',
'click': $this.toggleRendering.bind($this)
}).appendTo(buttons);
this.dom_element.append(buttons);
};
ModelViewer.prototype.initCamera = function () {
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, .1, 300);
this.camera.position.z = 80;
this.camera.position.y = -45;
this.camera.position.x = 35;
this.camera.up = new THREE.Vector3(0, 0, 1);
};
ModelViewer.prototype.initScene = function() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color( this.background );
this.scene.fog = new THREE.FogExp2(this.background, 0.06);
this.initLights();
this.initCamera();
var groundPlaneMaterial = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
wireframe: false,
transparent: true,
opacity: 0.25,
fog: false,
specular: 0x999999,
shininess: 100
});
this.plane = new THREE.Mesh(new THREE.PlaneGeometry(80,80), groundPlaneMaterial);
this.plane.receiveShadow = true;
this.scene.add(this.plane);
this.grid = new THREE.GridHelper( 80, 100, 0x000000, 0xAAAAAA);
this.grid.rotation.x = Math.PI/2;
this.scene.add(this.grid);
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(this.width, this.height);
this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1);
this.container.append(this.renderer.domElement);
this.controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
this.controls.enableZoom = true;
};
ModelViewer.prototype.initLights = function () {
this.spotLight = new THREE.SpotLight(0xffffff, 0.8, 0);
this.spotLight.position.set(-100, 100, 100);
this.spotLight.castShadow = false;
this.scene.add(this.spotLight);
this.bottomSpotLight = new THREE.SpotLight(0xffffff, 0.8, 0);
this.bottomSpotLight.position.set(0, -10, -100);
this.bottomSpotLight.castShadow = false;
this.scene.add(this.bottomSpotLight);
this.ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
this.scene.add(this.ambientLight);
this.pointLight = new THREE.PointLight(0xfdfdfd, 1, 0);
this.pointLight.position.set(32, -39, 35);
this.pointLight.castShadow = true;
this.scene.add(this.pointLight);
};
ModelViewer.prototype.addModel = function(geometry) {
var material = new THREE.MeshPhongMaterial({
color: 0x136fc3,
specular: 0x0D0D0D,
shading: THREE.SmoothShading,
shininess: 30,
fog: false,
side: THREE.DoubleSide,
wireframe: this.wireframe,
});
geometry.center();
var mesh = new THREE.Mesh(geometry, material);
mesh.scale.set(this.scale, this.scale, this.scale);
mesh.geometry.computeFaceNormals();
mesh.geometry.computeVertexNormals();
mesh.rotation.set(-Math.PI/2,0, 0);
mesh.geometry.computeBoundingBox();
mesh.castShadow = true;
// mesh.receiveShadow = true;
var dims = mesh.geometry.boundingBox.max.clone().sub(mesh.geometry.boundingBox.min);
maxDim = Math.max(Math.max(dims.x, dims.y), dims.z);
mesh.position.x = -(mesh.geometry.boundingBox.min.x + mesh.geometry.boundingBox.max.x) / 2 * this.scale;
mesh.position.z = -(mesh.geometry.boundingBox.min.y + mesh.geometry.boundingBox.max.y) / 2 * this.scale;
mesh.position.y = -mesh.geometry.boundingBox.min.z * this.scale;
var positionY = (mesh.geometry.boundingBox.max.z + mesh.geometry.boundingBox.min.z)/2 * this.scale;
var positionZ = (mesh.geometry.boundingBox.max.y - mesh.geometry.boundingBox.min.y)/2 * this.scale;
mesh.position.set(0, positionY, positionZ);
this.object = mesh;
this.scene.add(mesh);
this.centerCamera(mesh);
};
ModelViewer.prototype.loadStl = function(model) {
var self = this;
var loader = new THREE.STLLoader();
this.loadStl = function(model) {
var loader = new THREE.STLLoader();
loader.load(model, function (geometry) { loader.load(model, function (geometry) {
var material = new THREE.MeshPhongMaterial({color: 0xaaaaaa, shininess:200, specular: 0x333333, shading: THREE.FlatShading}); self.addModel(geometry);
}
);
};
var mesh = new THREE.Mesh(geometry, material); ModelViewer.prototype.centerCamera = function(mesh) {
mesh.geometry.computeBoundingBox(); var boxHelper = new THREE.BoxHelper( mesh );
var positionY = (mesh.geometry.boundingBox.max.y + mesh.geometry.boundingBox.min.y)/2;
// mesh.position.set(0, positionY, 0); var sceneCenter = this.objectCenter(mesh);
mesh.rotation.set(Math.PI, 0, 0);
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
renderer.render(scene, camera);
});
};
this.animate = function() { var geometry = mesh.geometry;
requestAnimationFrame( this.animate );
this.render();
};
this.render = function() { var distanceX = ((geometry.boundingBox.max.x - geometry.boundingBox.min.x) / 2) / Math.tan(this.camera.fov * this.camera.aspect * Math.PI / 360);
var distanceY = (geometry.boundingBox.max.y - geometry.boundingBox.min.y) / 2 / Math.tan(this.camera.fov * this.camera.aspect * Math.PI / 360);
var distanceZ = (geometry.boundingBox.max.z - geometry.boundingBox.min.z) / 2 / Math.tan(this.camera.fov * Math.PI / 360);
renderer.render(scene, camera); var maxDistance = Math.max(Math.max(distanceX, distanceY), distanceZ);
maxDistance *= 1.9 * this.scale;
}; var cameraPosition = this.camera.position.normalize().multiplyScalar(maxDistance);
this.controls.maxDistance = 3 * maxDistance;
this.controls.position0 = cameraPosition;
this.controls.target0 = sceneCenter;
this.controls.reset();
};
ModelViewer.prototype.toggleRendering = function () {
if($this.rendering) {
$this.container.style.display = "none";
$this.rendering = false;
$this.toggleButton.html('<i class="cube icon"/>3D');
$this.wireframeButton.hide();
} else {
$this.container.style.display = "block";
$this.rendering = true;
$this.toggleButton.html('<i class="close icon"/>Close');
$this.wireframeButton.show();
}
};
ModelViewer.prototype.toggleMaterial = function () {
if($this.wireframe) {
$this.wireframe = false;
$this.wireframeButton.html('<i class="eye icon"/>Wireframe')
} else {
$this.wireframe = true;
$this.wireframeButton.html('<i class="eye icon"/>Solid')
}
this.scene.traverse(function(object) {
if (object instanceof THREE.Mesh) {
object.material.wireframe = $this.wireframe;
}
});
};
ModelViewer.prototype.objectCenter = function (mesh) {
var middle = new THREE.Vector3();
var geometry = mesh.geometry;
geometry.center();
geometry.computeBoundingBox();
middle.x = (geometry.boundingBox.max.x + geometry.boundingBox.min.x) / 2;
middle.y = (geometry.boundingBox.max.y + geometry.boundingBox.min.y) / 2;
middle.z = (geometry.boundingBox.max.z + geometry.boundingBox.min.z) / 2;
mesh.localToWorld(middle);
return middle;
};
ModelViewer.prototype.resize = function(width, height) {
this.width = width;
this.height = height;
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
this.renderer.setSize(width, height);
};
ModelViewer.prototype.render = function() {
this.renderer.render(this.scene, this.camera);
}; };

View File

@ -2,4 +2,27 @@ $(document).ready(function () {
$('.ui.dropdown') $('.ui.dropdown')
.dropdown() .dropdown()
; ;
$('.image.load img')
.visibility({
type : 'image',
transition : 'fade in',
duration : 1000
})
;
$('.ui.rating')
.rating("disable")
;
$('.tabular.menu .item').tab();
$('.message .close')
.on('click', function() {
$(this)
.closest('.message')
.transition('fade')
;
})
;
}); });

View File

@ -1,3 +1,43 @@
.ui.fixed + .ui.main { .default-theme {
margin-top: 5em;
.ui.fixed + .ui.grid {
border-radius: 0;
border: 0;
box-shadow: none;
}
.item-info {
min-height: 300px;
}
.ui.fixed + .ui.main {
margin-top: 3em;
padding-bottom: 4em;
}
.link {
padding-left: 30px;
position: relative;
}
.link:before {
width: 23px;
height: 23px;
position: absolute;
left: 0;
display: inline-block;
}
.link.brickset:before {
content: '';
background: url("/resources/images/brickset_logo.png");
background-size: cover;
}
.link.rebrickable:before {
content: '';
background: url("/resources/images/rebrickable_logo.png");
background-size: cover;
}
} }

View File

@ -0,0 +1,34 @@
.model-container {
width: 100%;
position: relative;
border: 1px solid #DDDDDD;
img {
width: 100%;
height: 100%;
}
.modelviewer-buttons {
text-align: right;
position: absolute;
top: 0;
height: 35px;
width: 100%;
padding: 3px 4px;
> button {
border: none;
height: 100%;
margin-left: 5px;
padding: 0 15px;
}
}
.model-view {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}

View File

@ -1,4 +1,4 @@
@import "variables"; @import "variables";
@import "main"; @import "main";
@import "modelviewer";

View File

@ -46,6 +46,8 @@
4168950: 3815c01 4168950: 3815c01
73418: 3815c01 73418: 3815c01
82359: 3626b
122c02: 122c01 122c02: 122c01
# Bucket 1 x 1 x 1 Cylindrical # Bucket 1 x 1 x 1 Cylindrical
@ -119,6 +121,22 @@ u9147p01c06: u9147p01c02
u9147p02c02: u9147p01c02 u9147p02c02: u9147p01c02
u9147p03c02: u9147p01c02 u9147p03c02: u9147p01c02
# Figure Fabuland Monkey
u595p01c08: u595p01c04
u595p01c07: u595p01c04
u595p01c06: u595p01c04
u595p01c05: u595p01c04
# Figure Fabuland Monkey Head
u595p02c03: u595p02c02
# Figure Fabuland Monkey 2
u595p02c05: u595p02c04
# Figure Fabuland Mouse 1
u9105p01c06: u9105p01c02
u9105p01c03: u9105p01c02
# Figure Fabuland Elephant Head # Figure Fabuland Elephant Head
u588p02c02: u588p01c02 u588p02c02: u588p01c02
@ -164,4 +182,39 @@ u588p02c02: u588p01c02
# Hinge Control Stick and Base # Hinge Control Stick and Base
73587: 4592c01 73587: 4592c01
4296152: 4592c01 4296152: 4592c01
4505: 4505a
6093: 6093a
90462a: 90462
# Minifig Mechanical SW Battle Droid
30375cs1: 30375cs0
30375cs2: 30375cs0
30375cs3: 30375cs0
2958c01: 2723
32181c03: 32181c01
76019: 76244
4107539: 76244
3680c02: 3680c01
4221774: 3680c01
9245: 3680c01
9246: 3680c01
9258: 3680c01
9328: 3680c01
9352: 3680c01
4508c01: 251c01
4100338: 2557c01
4100340: 6051c01
4100339: 2559c01
75192: 2977c01
4539364: 64776

View File

@ -89,7 +89,6 @@
10119: 51704 10119: 51704
3149: 3149d 3149: 3149d
75998: 4493c00 75998: 4493c00
10119: 51704
30658: 3404 30658: 3404
59275: 2599 59275: 2599
@ -153,4 +152,59 @@ wheel2a: 568c01
11895pr0001c01: 11895 11895pr0001c01: 11895
92456pr0021c01: 92241p03c01 92456pr0021c01: 92241p03c01
15672: 92946 15672: 92946
3614a: 3614
3614b: 3614
75215: 4694c01
6093: 6093a
3626bpr0895: 82359
6272c01: 6272
76320: 32181c02
95292c01: 75348
2909c03: 75348
2909c02: 75348
# Technic figures
tech001: 2698c01
tech002: 2698c01
tech003: 2698c01
tech004: 2698c01
tech005: 2698c01
tech006: 2698c01
tech007: 2698c01
tech008: 2698c01
tech009: 2698c01
tech010: 2698c01
tech011: 2698c01
tech011a: 2698c01
tech012: 2698c01
tech013: 2698c01
tech014: 2698c01
tech014a: 2698c01
tech015: 2698c01
tech016: 2698c01
tech016a: 2698c01
tech017: 2698c01
tech017a: 2698c01
tech018: 2698c01
tech019: 2698c01
tech020: 2698c01
tech021: 2698c01
tech022: 2698c01
tech023: 2698c01
tech024: 2698c01
tech025: 2698c01
tech026: 2698c01
tech027: 2698c01
tech028: 2698c01
tech029: 2698c01
33299a: 33299
33299b: 33299

View File

@ -1,40 +1,48 @@
{% extends 'html.html.twig' %}
{% import 'macros/elements.html.twig' as elements %} {% import 'macros/elements.html.twig' as elements %}
<!DOCTYPE html> {% block body %}
<html> <div class="ui fixed inverted menu">
<head> {{ knp_menu_render('mainMenu') }}
<meta charset="UTF-8" /> </div>
<title>{% block title %}Welcome!{% endblock %}</title> {% block page %}
{% block stylesheets %} <div class="ui main">
<link rel="stylesheet" href="{{ asset('resources/css/main.css') }}"> <div class="ui container">
{% endblock %} <div class="ui masthead vertical segment">
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" /> <div class="introduction">
</head> <div class="ui small breadcrumb">
<body> {% for breadcrumb_item in knp_menu_get_breadcrumbs_array(knp_menu_get_current_item('mainMenu')) %}
{% block body %} {% if not loop.last %}
<div class="ui fixed inverted menu"> <a class="section" href="{{ breadcrumb_item.uri }}">{{ breadcrumb_item.label }}</a>
{{ knp_menu_render('mainMenu') }} <i class="right chevron icon divider"></i>
{% else %}
<a class="active section">{{ breadcrumb_item.label }}</a>
{% endif %}
{% endfor %}
</div>
<h1 class="ui header">{% block header %}{% endblock %}</h1>
</div>
</div>
<div class="ui vertical segment content">
{% for label, flashes in app.session.flashbag.all %}
{% for flash in flashes %}
{{ elements.flash(label,flash) }}
{% endfor %}
{% endfor %}
{% block content %}
{% endblock %}
</div>
</div> </div>
<div class="ui main container"> </div>
{% for label, flashes in app.session.flashbag.all %} {% endblock page %}
{% for flash in flashes %} <div class="ui black inverted vertical footer segment stripe">
{{ elements.flash(label,flash) }} {% block footer %}
{% endfor %} <div class="ui center aligned container">
{% endfor %}
{% block content %}
{% endblock %}
</div> </div>
{% endblock %} {% endblock %}
</div>
{% block javascripts %} {% endblock %}
<script type="text/javascript" src="{{ asset('resources/js/three.js') }}"></script>
<script type="text/javascript" src="{{ asset('resources/js/OrbitControls.js') }}"></script>
<script src="{{ asset('resources/js/main.js') }}"></script>
{% endblock %}
</body>
</html>

View File

@ -14,12 +14,14 @@
</div> </div>
{{ form_end(form) }} {{ form_end(form) }}
<h3>{{ sets|length }}</h3>
<div class="ui seven column grid"> <div class="ui seven column grid">
<div class="row"> <div class="row">
{% for set in sets %} {% for set in sets %}
<div class="column"> <div class="column">
<a href="{{ url('set_detail', {'number': set.number~'-'~set.numberVariant, 'name' : set.name|escape('url') }) }}"> <a href="{{ url('set_detail', {'number': set.number~'-'~set.numberVariant, 'name' : set.name|escape('url') }) }}">
<img style="width: 100%" src="{{ set.largeThumbnailURL }}"> <img style="width: 100%" src="{{ set.legoSetID|setImage|imagine_filter('rebrickable_set_min') }}">
<p>{{ set.LegoSetID }} - {{ set.name }}</p> <p>{{ set.LegoSetID }} - {{ set.name }}</p>
</a> </a>
</div> </div>

View File

@ -0,0 +1,9 @@
<div class="ui six doubling cards">
{% for image in images %}
<div class="card">
<a class="ui bordered fluid image" href="{{ image.imageURL }}" data-lightbox="setImages">
<img src="{{ image.thumbnailURL }}">
</a>
</div>
{% endfor %}
</div>

View File

@ -0,0 +1,30 @@
{% import 'macros/utils.html.twig' as utils %}
{% if instructions|length != 0 %}
<p>
{{ 'set.instructions.text' | trans }}
</p>
<table class="ui celled padded table">
<thead>
<tr>
<th>{{ 'set.instructions.description' | trans }}</th>
<th>{{ 'set.instructions.filesize' | trans }}</th>
<th>{{ 'set.instructions.filename' | trans }}</th>
</tr>
</thead>
<tbody>
{% for instruction in instructions %}
<tr>
<td>{{ instruction.description }}</td>
<td>{{ utils.bytesToSize( remoteSize(instruction.uRL)) }}</td>
<td><a href="{{ instruction.uRL }}">{{ remoteFilename(instruction.uRL) }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<h2 class="ui center aligned icon">
<i class="circular warning icon"></i>
</h2>
{% endif %}

View File

@ -0,0 +1,26 @@
<div class="ui comments">
{% for review in reviews %}
<div class="comment">
<a class="avatar">
{#<img src="/images/avatar/small/matt.jpg">#}
</a>
<div class="content">
<div class="header">
<h4>{{ review.title }}</h4>
</div>
<span class="author">{{ review.author }}</span> {{ review.hTML }}
<div class="metadata">
<span class="date">{{ review.datePosted|date("Y.m.d H:m:i")}}</span>
</div>
<div class="text">
{{ review.review|raw }}
</div>
<div>
Overall rating <div class="ui star rating" data-rating="{{ review.overallRating }}" data-max-rating="5"></div>
{#Value for money <div class="ui star rating" data-rating="{{ review.valueForMoney }}" data-max-rating="5"></div>#}
</div>
</div>
</div>
{% endfor %}
</div>

View File

@ -1,6 +1,14 @@
{% extends 'base.html.twig' %} {% extends 'base.html.twig' %}
{% block content %} {% block page %}
<div class="ui grid massive message vertical">
<div class="ui container">
<div class="row">
<div class="h1 ui huge header">
Hello, world!
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -3,9 +3,9 @@
{% block title %}{{ 'page.error.title'|trans }}{% endblock %} {% block title %}{{ 'page.error.title'|trans }}{% endblock %}
{% block content %} {% block content %}
<h2 class="ui center aligned icon header"> <h1 class="ui center aligned icon header">
<i class="circular warning icon"></i> <i class="circular warning icon"></i>
{{ 'page.error.large'|trans }} {{ 'page.error.large'|trans }}
</h2> </h1>
<p class="ui center aligned">{{ 'page.error.text'|trans }}</p> <p class="ui center aligned">{{ 'page.error.text'|trans }}</p>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}PrintABrick{% endblock %}</title>
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('resources/css/main.css') }}">
{% endblock %}
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body class="default-theme">
{% block body %}
{% endblock %}
{% block javascripts %}
<script type="text/javascript" src="{{ asset('resources/js/three.js') }}"></script>
<script type="text/javascript" src="{{ asset('resources/js/OrbitControls.js') }}"></script>
<script src="{{ asset('resources/js/main.js') }}"></script>
{% endblock %}
</body>
</html>

View File

@ -1,85 +0,0 @@
{% extends 'base.html.twig' %}
{% import 'macros/elements.html.twig' as elements %}
{% block content %}
{% if model %}
<dl>
<dt>number:</dt><dd>{{ model.number }}</dd>
<dt>name:</dt><dd>{{ model.name }}</dd>
<dt>category:</dt><dd>{{ model.category ? model.category.name }}</dd>
<dt>type:</dt><dd>{{ model.type ? model.type.name }}</dd>
<dt>model:</dt><dd>{{ model.path }}</dd>
<dt>author:</dt><dd>{{ model.author }}</dd>
<dt>keywords:</dt>
<dd>
{% for keyword in model.keywords %}
<span class="ui label">{{ keyword.name }}</span>
{% endfor %}
</dd>
<dt>aliases:</dt>
<dd>
{% for alias in model.aliases %}
<span class="ui label">{{ alias.number }}</span>
{% endfor %}
</dd>
<dd>Download:</dd>
<dt><a href="{{ path('model_stl', {'number' : model.number })}}">{{ model.number }}</a></dt>
<dt>rebrickable parts ({{ rbParts|length }}):</dt>
<dd>
<p>
<div class="ui eight doubling cards">
{% for alias in rbParts %}
<a href="{{ url('rebrickable_part_show', {number:alias.number}) }}" class="ui label">{{ alias.number }}</a>
{% endfor %}
</div>
</p>
</dd>
</dl>
{% endif %}
<div style="display: flex; flex-wrap: wrap">
<div id="model" style="height: 300px; width: 300px; padding: 5px; display: inline-block"></div>
<div style="height: 300px; width: 300px; padding: 5px; display: inline-block">
<img src="{{ url('model_image', {'number': model.number}) }}" style="max-height: 90%; max-width: 100%">
</div>
<h4 class="ui horizontal divider header">
<i class="puzzle icon"></i> Subparts of this model
</h4>
{% for subpart in model.subparts %}
{{ elements.part(subpart.subpart) }}
{% endfor %}
<h4 class="ui horizontal divider header">
<i class="cube icon"></i> Model is subpart of
</h4>
{% for subpart in model.parents %}
{{ elements.part(subpart.parent) }}
{% endfor %}
<h4 class="ui horizontal divider header">
<i class="cubes icon"></i> Sets ({{ sets|length }})
</h4>
{% for set in sets %}
<span style="margin: 5px"><a href="{{ url('set_detail', {number:set.number}) }}">{{ set.number }}</a></span>
{% endfor %}
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script type="text/javascript">
window.onload = function() {
modelView = new ModelViewer();
var scene = modelView.initScene($('#model'));
modelView.loadStl('{{ path('model_stl', {'number' : model.number })}}');
modelView.render();
};
</script>
{% endblock %}

View File

@ -1,7 +1,13 @@
{% macro part(model) %} {% macro part(model) %}
<div style="height: 100px; width: 100px; padding: 5px; display: inline-block;"> <div class="column">
<img src="{{ url('model_image', {'number': model.number }) }}" style="max-height: 90%; max-width: 100%;"> <div class="ui bordered fluid image">
<p><a href="{{ url('model_detail', {'number': model.number}) }}">{{ model.number }}</a></p> <a href="{{ url('model_detail', {'number': model.number})}}">
<div class="image">
<img src="{{ asset('/ldraw/images/'~model.number~'.png') | imagine_filter('model_min') }}" data-src="{{ asset('/ldraw/images/'~model.number~'.png') | imagine_filter('model_min') }}" class="transition visible">
</div>
<div class="ui bottom attached label {% if model.parts|length == 0 %}black{% endif %}">{{ model.number }}</div>
</a>
</div>
</div> </div>
{% endmacro %} {% endmacro %}

View File

@ -0,0 +1,21 @@
{#http://stackoverflow.com/a/15303004#}
{% macro bytesToSize(bytes) %}
{% spaceless %}
{% set kilobyte = 1024 %}
{% set megabyte = kilobyte * 1024 %}
{% set gigabyte = megabyte * 1024 %}
{% set terabyte = gigabyte * 1024 %}
{% if bytes < kilobyte %}
{{ bytes ~ ' B' }}
{% elseif bytes < megabyte %}
{{ (bytes / kilobyte)|number_format(2, '.') ~ ' KB' }}
{% elseif bytes < gigabyte %}
{{ (bytes / megabyte)|number_format(2, '.') ~ ' MB' }}
{% elseif bytes < terabyte %}
{{ (bytes / gigabyte)|number_format(2, '.') ~ ' GB' }}
{% else %}
{{ (bytes / terabyte)|number_format(2, '.') ~ ' TB' }}
{% endif %}
{% endspaceless %}
{% endmacro %}

View File

@ -0,0 +1,122 @@
{% extends 'base.html.twig' %}
{% import 'macros/elements.html.twig' as elements %}
{% block title %}#{{ model.number }} - {{ model.name }}{% endblock %}
{% block header %}#{{ model.number }} - {{ model.name }}{% endblock %}
{% block content %}
<div class="ui grid">
<div class="column ten wide">
<div id="model-viewer" class="model-container">
<img src="{{ asset('ldraw/images/'~model.number~'.png') | imagine_filter('model_large')}}">
</div>
</div>
<div class="column six wide">
<div class="grey">
<table>
<tr>
<th>category</th><td>{{ model.category ? model.category.name }}</td>
</tr>
<tr>
<th>model</th><td>{{ model.path }}</td>
</tr>
<tr>
<th>author</th><td>{{ model.author.name }}</td>
</tr>
</table>
<dl>
<dt>keywords:</dt>
<dd>
{% for keyword in model.keywords %}
<span class="ui label">{{ keyword.name }}</span>
{% endfor %}
</dd>
<dt>aliases:</dt>
<dd>
{% for alias in model.aliases %}
<span>{{ alias.number }}</span>{% if not loop.last %},{% endif %}
{% endfor %}
</dd>
<dd>Download:</dd>
<dt><a href="{{ url('media_file', {'path': model.path}) }}">{{ model.number }}</a></dt>
<dt>rebrickable parts ({{ rbParts|length }}):</dt>
<dd>
{#<p>#}
{#<div class="ui eight doubling cards">#}
{#{% for alias in rbParts %}#}
{#<a href="{{ url('reb_part_detail', {number:alias.number}) }}" class="ui label">{{ alias.number }}</a>#}
{#{% endfor %}#}
{#</div>#}
{#</p>#}
</dd>
</dl>
</div>
</div>
</div>
<h4 class="ui horizontal divider header">
Related Models
</h4>
<h5 class="ui horizontal divider header">
Subparts of this model
</h5>
<div class="ui eight column grid">
{% for subpart in model.subparts %}
<div class="column">
{{ elements.part(subpart.subpart) }}
</div>
{% endfor %}
</div>
<h5 class="ui horizontal divider header">
Model is subpart of
</h5>
<div class="ui eight column grid">
{% for subpart in model.parents %}
<div class="column">
{{ elements.part(subpart.parent) }}
</div>
{% endfor %}
</div>
<h5 class="ui horizontal divider header">
Model pairs with
</h5>
<div class="ui eight column grid">
{% for subpart in related %}
<div class="column">
{{ elements.part(subpart) }}
</div>
{% endfor %}
</div>
<h4 class="ui horizontal divider header">
<i class="cubes icon"></i> Sets ({{ sets|length }})
</h4>
{% for set in sets %}
<span style="margin: 5px"><a href="{{ url('set_detail', {number:set.number}) }}">{{ set.number }}</a></span>
{% endfor %}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script type="text/javascript">
window.onload = function() {
modelView = new ModelViewer($('#model-viewer'), '{{ url('media_file', {'path': model.path }) }}');
};
</script>
{% endblock %}

View File

@ -2,8 +2,14 @@
{% import 'macros/elements.html.twig' as elements %} {% import 'macros/elements.html.twig' as elements %}
{% block title %}{{ 'page.model.index' | trans }}{% endblock %}
{% block header %}{{ 'page.model.index' | trans }}{% endblock %}
{% block content %} {% block content %}
<form method="get" action="." class="ui form"> <div class="ui container stackable grid">
<div class="column four wide">
<form method="get" action="" class="ui form">
{{ form_start(form) }} {{ form_start(form) }}
{{ form_row(form.search) }} {{ form_row(form.search) }}
@ -16,18 +22,17 @@
</div> </div>
{{ form_end(form) }} {{ form_end(form) }}
</form> </form>
</div>
<div class="column twelve wide">
{{ knp_pagination_render(models) }}
<p>{{ models.getTotalItemCount }}</p> <p>{{ models.getTotalItemCount }}</p>
<div class="ui ten column grid"> <div class="ui eight column grid">
<div class="row"> {% for model in models %}
{% for model in models %} {{ elements.part(model) }}
<div class="column"> {% endfor %}
{{ elements.part(model) }}
</div>
{% endfor %}
</div>
</div> </div>
{{ knp_pagination_render(models) }} {{ knp_pagination_render(models) }}
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -2,7 +2,7 @@
{% block content %} {% block content %}
<table class="ui celled padded table"> <table class="ui celled small padded table">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>

View File

@ -9,28 +9,33 @@
<dt>category:</dt><dd>{{ part.category ? part.category.name }}</dd> <dt>category:</dt><dd>{{ part.category ? part.category.name }}</dd>
</dl> </dl>
<div style="display: flex; flex-wrap: wrap">
{% if apiPart is not null %}
<div style="height: 300px; width: 300px; padding: 5px; display: inline-block">
<img src="{{ apiPart.imgUrl }}" style="max-height: 90%; max-width: 100%">
</div>
{% endif %}
<div style="height: 300px; width: 300px; padding: 5px; display: inline-block">
<img src="{{ url('model_image', {number:part.number}) }}" style="max-height: 90%; max-width: 100%">
</div>
{% if part.model %} {% if apiPart is not null %}
{{ elements.part(part.model) }} <a href="{{ apiPart.url }}">Rebrickable</a>
{% endif %}
</div> <img class="image ui middle" src="{{ apiPart.imgUrl }}" style="max-height: 90%; max-width: 100%">
{% endif %}
{% if part.model %}
{{ elements.part(part.model) }}
{% endif %}
<h4 class="ui horizontal divider header"> <h4 class="ui horizontal divider header">
<i class="puzzle icon"></i> Sets <i class="puzzle icon"></i> Sets
</h4> </h4>
<div class="ui eight column grid">
{% for set in sets %} {% for set in sets %}
<span style="margin: 5px"><a href="{{ url('set_detail', {number:set.number}) }}">{{ set.number }}</a></span> <div class="column">
<div class="ui fluid bordered image">
<a href="{{ url('set_detail', {number:set.number}) }}">
<img class="ui bordered image" src="{{ set.number|setImage|imagine_filter('rebrickable_set_min') }}">
<div class="ui bottom attached label">{{ set.number }}<br></div>
</a>
</div>
</div>
{% endfor %} {% endfor %}
<div>
{{ dump(apiPart) }} {{ dump(apiPart) }}

View File

@ -0,0 +1,25 @@
{% extends 'base.html.twig' %}
{% block content %}
<div class="ui seven column grid">
<div class="row">
{% for part in parts %}
<div class="column">
<div class="ui fluid bordered image">
<a href="{{ path('reb_part_detail', {number: part.number}) }}">
<div class="image load">
<img src="{{ part.number|partImage(-1)| imagine_filter('rebrickable_part_min') }}" data-src="{{ part.number|partImage(-1)| imagine_filter('rebrickable_part_min') }}" class="transition visible">
</div>
<div class="ui bottom attached label {{ part.model == null ? 'black' : 'red'}}">{{ part.number }}</div>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
<p>{{ parts.getTotalItemCount }}</p>
{{ knp_pagination_render(parts) }}
{% endblock %}

View File

@ -1,58 +0,0 @@
{% extends 'base.html.twig' %}
{% block content %}
{% if set is not null %}
<dl>
<dt>number:</dt><dd>{{ set.number }}</dd>
<dt>year:</dt><dd>{{ set.year }}</dd>
<dt>name:</dt><dd>{{ set.name }}</dd>
<dt>theme:</dt><dd>{{ set.theme.name }}</dd>
<dt>count of parts:</dt><dd>{{ set.partCount }}</dd>
</dl>
<img class="ui bordered image medium" src="{{ rbset ? rbset.imgUrl }}">
<h4 class="ui horizontal divider header">
<i class="cubes icon"></i> Parts {{ inventoryParts|length }}
</h4>
<div class="ui eight column grid">
{% for inventoryPart in inventoryParts %}
<div class="column">
<div class="ui fluid image {{ inventoryPart.part.model == null ? 'active dimmer'}}" style="height: 150px;">
<a class="ui right red circular label">{{ inventoryPart.quantity }}</a>
<a href="{{ path('rebrickable_part_show', {number: inventoryPart.part.number}) }}">
<img src="https://rebrickable.com/media/parts/ldraw/{{inventoryPart.color ? inventoryPart.color.id }}/{{ inventoryPart.part.number }}.png">
<div style="border-bottom: #{{ inventoryPart.color.rgb }} 5px solid" class="ui bottom attached label">{{ inventoryPart.part.number }}<br>{{ inventoryPart.isSpare ? 'Spare' : 'Regular' }}</div>
</a>
</div>
</div>
{% endfor %}
<div>
<h4 class="ui horizontal divider header">
<i class="cubes icon"></i> Models {{ parts|length }}
</h4>
<div class="ui eight column grid">
{% for part in parts %}
<div class="column">
<div class="ui bordered fluid image">
<a href="{{ path('model_detail', {number: part.number}) }}">
<img src="https://rebrickable.com/media/parts/ldraw/-1/{{ part.number }}.png">
<div class="ui bottom attached label">{{ part.number }} </div>
</a>
</div>
</div>
{% endfor %}
<div>
{% endif %}
{{ brset ? dump(brset) }}
{% endblock %}

View File

@ -1,21 +0,0 @@
{% extends 'base.html.twig' %}
{% block content %}
<div class="ui seven column grid">
<div class="row">
{% for set in sets %}
<div class="column">
<a href="{{ url('set_detail', {'number': set.number, 'name' : set.name|escape('url') }) }}">
{#<img style="width: 100%" src="{{ set }}">#}
<p>{{ set.number }} - {{ set.name }}</p>
</a>
</div>
{% endfor %}
</div>
</div>
<p>{{ sets.getTotalItemCount }}</p>
{{ knp_pagination_render(sets) }}
{% endblock %}

View File

@ -0,0 +1,46 @@
{% if regularParts|length > 0 %}
<h4 class="ui horizontal divider header">
Regular parts
</h4>
<div class="ui ten column grid">
{% for inventoryPart in regularParts %}
{% if inventoryPart.part is defined %}
<div class="column">
<div class="ui fluid bordered image">
<a class="ui right {{ inventoryPart.part.model == null ? 'black' : 'red'}} circular label">{{ inventoryPart.quantity }}</a>
<a href="{{ path('reb_part_detail', {number: inventoryPart.part.number}) }}">
<div class="image load">
<img src="{{ inventoryPart.part.number|partImage(inventoryPart.color.id)| imagine_filter('rebrickable_part_min') }}" data-src="{{ inventoryPart.part.number|partImage(inventoryPart.color.id)| imagine_filter('rebrickable_part_min') }}" class="transition visible">
</div>
<div style="border-bottom: #{{ inventoryPart.color.rgb }} 5px solid" class="ui bottom attached label">{{ inventoryPart.part.number }}</div>
</a>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if spareParts|length > 0 %}
<h4 class="ui horizontal divider header">
Spare parts
</h4>
<div class="ui ten column grid">
{% for inventoryPart in spareParts %}
{% if inventoryPart.part is defined %}
<div class="column">
<div class="ui fluid bordered image">
<a class="ui right {{ inventoryPart.part.model == null ? 'black' : 'red'}} circular label">{{ inventoryPart.quantity }}</a>
<a href="{{ path('reb_part_detail', {number: inventoryPart.part.number}) }}">
<div class="image load">
<img src="{{ inventoryPart.part.number|partImage(inventoryPart.color.id)| imagine_filter('rebrickable_part_min') }}" data-src="{{ inventoryPart.part.number|partImage(inventoryPart.color.id)| imagine_filter('rebrickable_part_min') }}" class="transition visible">
</div>
<div style="border-bottom: #{{ inventoryPart.color.rgb }} 5px solid" class="ui bottom attached label">{{ inventoryPart.part.number }}</div>
</a>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}

View File

@ -0,0 +1,16 @@
<h4 class="ui horizontal divider header">Sets</h4>
<div class="ui seven column grid">
<div class="row">
{% for set in inventorySets %}
<div class="column">
<a href="{{ url('set_detail', {'number': set.set.number}) }}">
<div class="ui bordered image medium">
<img src="{{ asset('resources/images/unknown_image.png') }}" data-src="{{ set.set.number|setImage|imagine_filter('rebrickable_set_min') }}" class="transition visible">
</div>
<p>{{ set.set.number }} - {{ set.set.name }}</p>
<p>{{ set.quantity}}</p>
</a>
</div>
{% endfor %}
</div>
</div>

View File

@ -0,0 +1,138 @@
{% extends 'base.html.twig' %}
{% block title %}{{ rbset ? rbset.number }} {{ rbset ? rbset.name }}{% endblock %}
{% block header %}{{ rbset ? rbset.number }} {{ rbset ? rbset.name }}{% endblock %}
{% block content %}
<div class="ui stackable grid">
<div class="column nine wide">
<div class="image bordered ui big">
{% if brset %}
<img class="big" src="{{ brset.imageURL }}">
{% elseif rbset %}
<img class="big" src="{{ rbset.number|setImage|imagine_filter('rebrickable') }}">
{% endif %}
</div>
</div>
<div class="column seven wide">
<div class="item-info black ui">
<table class="ui table">
<tr>
<td>number</td><td>{{ brset ? brset.legoSetID : rbset ? rbset.number : null}}</td>
</tr>
<tr>
<td>name</td><td>{{ brset ? brset.name : rbset ? rbset.name : null}}</td>
</tr>
<tr>
<td>year</td><td>{{ brset ? brset.year : rbset ? rbset.year : null}}</td>
</tr>
<tr>
<td>theme</td>
{% if rbset %}
<td><a href="#">{{ rbset.theme.parent ? rbset.theme.parent.name }}</a> <a href="#">{{ rbset.theme.name }}</a> </td>
{% elseif brset %}
<td>{{ brset.theme }}</td>
{% endif %}
</tr>
<tr>
<td>parts</td><td>{{ brset ? brset.pieces : rbset ? rbset.partCount }}</td>
</tr>
{% if brset %}
<tr>
<td></td>
<td><a class="brickset link" href="{{ brset.bricksetURL }}">Brickset</a></td>
</tr>
{% endif %}
{% if rbset %}
<tr>
<td></td>
<td><a class="rebrickable link" href="http://rebrickable.com/sets/{{ rbset ? rbset.number }}">Rebrickable</a></td>
</tr>
{% endif %}
</table>
{% if brset is not null %}
<dl>
<dt>themegroup:</dt><dd>{{ brset.themeGroup }}</dd>
<dt>theme:</dt><dd>{{ brset.theme }}</dd>
<dt>subtheme:</dt><dd>{{ brset.subtheme }}</dd>
<dt>count of parts:</dt><dd>{{ brset.pieces }}</dd>
<dt>minifigs:</dt><dd>{{ brset.minifigs }}</dd>
</dl>
{% endif %}
</div>
</div>
</div>
<div class="segment vertical item-info">
<div class="ui tabular menu">
<a class="item active" data-tab="parts"><i class="cubes icon"></i> Parts ({{ rbset ? rbset.partCount : 0}})</a>
{% if brset %}
<a class="item" data-tab="description"><i class="info icon"></i> Description</a>
<a class="item" data-tab="images"> <i class="image icon"></i> Images ({{ brset.additionalImageCount }})</a>
<a class="item" data-tab="instructions"> <i class="file pdf outline icon"></i> Instructions ({{ brset.instructionsCount }})</a>
<a class="item" data-tab="reviews"><i class="write icon"></i> Reviews ({{ brset.reviewCount }})</a>
{% endif %}
</div>
<div class="ui tab active" data-tab="parts">
{% if rbset %}
<div class="ajax-load" id="parts" data-src="{{ path('rebrickable_set_parts', { 'number': rbset.number }) }}"></div>
{#{{ render(controller('AppBundle:Rebrickable/Set:parts', { 'number': rbset.number })) }}#}
{% endif %}
</div>
{% if brset %}
<div class="ui tab" data-tab="images">
{#<div class="ajax-load" id="images" data-src="{{ path('brickset_images', { 'id': brset.setID }) }}"></div>#}
{{ render(controller('AppBundle:Brickset/Set:images', { 'id': brset.setID })) }}
</div>
<div class="ui tab" data-tab="instructions">
{#<div class="ajax-load" id="instructions" data-src="{{ path('brickset_instructions', { 'id': brset.setID }) }}"></div>#}
{{ render(controller('AppBundle:Brickset/Set:instructions', { 'id': brset.setID })) }}
</div>
<div class="ui tab" data-tab="reviews">
{#<div class="ajax-load" id="reviews" data-src="{{ path('brickset_reviews', { 'id': brset.setID }) }}"></div>#}
{{ render(controller('AppBundle:Brickset/Set:reviews', { 'id': brset.setID })) }}
</div>
<div class="ui tab" data-tab="description">
{{ brset.description }}
</div>
{% endif %}
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script type="text/javascript">
$(document).ready(function () {
$('.ajax-load').each(function () {
$self = $(this);
console.log($self);
$.ajax({
type: "POST",
dataType: 'json',
url: $self.data('src'),
async: true, //you won't need that if nothing in your following code is dependend of the result
success: function(response){
$self.html(response); //Change the html of the div with the id = "your_div"
}
})
})
})
</script>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends 'base.html.twig' %}
{% block content %}
<form method="get" action="" class="ui form">
{{ form_start(form) }}
{{ form_row(form.search) }}
<div class="field fluid search selection">
{{ form_label(form.theme.id) }}
{{ form_widget(form.theme.id) }}
</div>
<div class="field">
<input class="ui submit button" type="submit" value="filter"/>
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
</form>
<div class="ui seven column grid">
<div class="row">
{% for set in sets %}
<div class="column">
<a href="{{ url('set_detail', {'number': set.number, 'name' : set.name|escape('url') }) }}">
<img class="ui bordered image medium" src="{{ set.number|setImage|imagine_filter('rebrickable_set_min') }}">
<p>{{ set.number }} - {{ set.name }}</p>
</a>
</div>
{% endfor %}
</div>
</div>
<p>{{ sets.getTotalItemCount }}</p>
{{ knp_pagination_render(sets) }}
{% endblock %}

View File

@ -1,7 +1,7 @@
<?php <?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Composer\Autoload\ClassLoader; use Composer\Autoload\ClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
/** @var ClassLoader $loader */ /** @var ClassLoader $loader */
$loader = require __DIR__.'/../vendor/autoload.php'; $loader = require __DIR__.'/../vendor/autoload.php';

2564
app/config/brickset.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,10 +7,8 @@ imports:
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration # http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters: parameters:
locale: en locale: en
# rebrickable csv files root URL # rebrickable csv files root URL (http://rebrickable.com/media/downloads/ or local dir containing csv files)
rebrickable_url: 'http://rebrickable.com/media/downloads/' rebrickable_csv_url: 'http://rebrickable.com/media/downloads/'
# LDraw library zip file URL
ldraw_url: 'http://www.ldraw.org/library/updates/complete.zip'
framework: framework:
#esi: ~ #esi: ~
@ -92,11 +90,56 @@ knp_paginator:
pagination: KnpPaginatorBundle:Pagination:semantic_ui_pagination.html.twig # sliding pagination controls template pagination: KnpPaginatorBundle:Pagination:semantic_ui_pagination.html.twig # sliding pagination controls template
sortable: KnpPaginatorBundle:Pagination:sortable_link.html.twig # sort link template sortable: KnpPaginatorBundle:Pagination:sortable_link.html.twig # sort link template
liip_imagine:
loaders:
media:
flysystem:
filesystem_service: oneup_flysystem.media_filesystem
rebrickable:
stream:
wrapper: 'http://m.rebrickable.com/media/'
resolvers:
default:
web_path: ~
filter_sets:
model_min:
data_loader: media
cache: ~
quality: 80
default_image: '/resources/images/unknown_image.png'
filters:
thumbnail: { size: [200, 200], mode: inset }
background: { size: [250, 250], position: center, color: '#FFFFFF' }
model_large:
data_loader: media
cache: ~
quality: 92
filters:
thumbnail: { size: [800, 600], mode: inset }
background: { size: [1100, 800], position: center, color: '#FFFFFF' }
rebrickable:
data_loader: rebrickable
rebrickable_part_min:
quality: 80
data_loader: rebrickable
cache: ~
default_image: '/resources/images/unknown_image.png'
filters:
thumbnail: { size: [250, 250], mode: inset }
rebrickable_set_min:
quality: 80
data_loader: rebrickable
default_image: '/resources/images/unknown_image.png'
cache: ~
filters:
thumbnail: { size: [250, 250], mode: inset }
oneup_flysystem: oneup_flysystem:
adapters: adapters:
media_adapter: media:
local: local:
directory: "%kernel.root_dir%/../var/media/" directory: "%kernel.root_dir%/../var/media/"
filesystems: filesystems:
media: media:
adapter: media_adapter adapter: media

View File

@ -12,15 +12,22 @@ web_profiler:
intercept_redirects: false intercept_redirects: false
monolog: monolog:
channels: ['loader']
handlers: handlers:
main: main:
type: stream type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log" path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug level: debug
channels: [!event] channels: [!event, !loader]
console: console:
type: console type: console
channels: [!event, !doctrine] channels: [!event, !doctrine]
loader:
type: rotating_file
path: "%kernel.logs_dir%/loader.log"
level: debug
channels: 'loader'
max_files: 10
# uncomment to get logging in your browser # uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration # you may have to allow bigger header sizes in your Web server configuration
#firephp: #firephp:

View File

@ -1,3 +1,5 @@
app: app:
resource: "@AppBundle/Controller/" resource: "@AppBundle/Controller/"
type: annotation type: annotation
_liip_imagine:
resource: "@LiipImagineBundle/Resources/config/routing.xml"

View File

@ -1,7 +1,7 @@
services: services:
api.client.brickset: api.client.brickset:
class: AppBundle\Api\Client\Brickset\Brickset class: AppBundle\Api\Client\Brickset\Brickset
arguments: ['%brickset_apikey%'] arguments: ['%brickset_apikey%', '%kernel.root_dir%/config/brickset.xml']
api.client.rebrickable: api.client.rebrickable:
class: AppBundle\Api\Client\Rebrickable\Rebrickable_v3 class: AppBundle\Api\Client\Rebrickable\Rebrickable_v3
arguments: ['%rebrickable_apikey%'] arguments: ['%rebrickable_apikey%']

View File

@ -1,7 +1,20 @@
services: services:
form.filter.category: form.filter.category:
class: AppBundle\Form\Filter\CategoryFilterType class: AppBundle\Form\Filter\Model\CategoryFilterType
arguments: arguments:
- '@manager.ldraw.category' - '@repository.ldraw.category'
tags: tags:
- { name: form.type } - { name: form.type }
form.filter.brickset:
class: AppBundle\Form\FilterSetType
arguments: ['@api.manager.brickset']
tags:
- { name: form.type }
form.filter.theme:
class: AppBundle\Form\Filter\Set\ThemeFilterType
arguments:
- '@repository.rebrickable.theme'
tags:
- { name: form.type }

View File

@ -1,29 +1,25 @@
services: services:
service.loader: service.loader.base:
abstract: true abstract: true
class: AppBundle\Service\Loader\BaseLoaderService class: AppBundle\Service\Loader\BaseLoader
calls: calls:
- [setArguments, ['@doctrine.orm.entity_manager', '@app.relation.mapper']] - [setArguments, ['@doctrine.orm.entity_manager', '@monolog.logger.loader']]
service.ldview: service.ldview:
class: AppBundle\Service\LDViewService class: AppBundle\Service\LDViewService
arguments: ['%ldview_bin%', '@oneup_flysystem.media_filesystem'] arguments: ['%ldview_bin%', '@oneup_flysystem.media_filesystem']
service.loader.rebrickable: service.loader.rebrickable:
class: AppBundle\Service\Loader\RebrickableLoaderService class: AppBundle\Service\Loader\RebrickableLoader
arguments: ['%rebrickable_url%'] arguments: ['%rebrickable_csv_url%']
parent: service.loader parent: service.loader.base
util.dat.parser: service.loader.model:
class: AppBundle\Utils\DatParser class: AppBundle\Service\Loader\ModelLoader
arguments: ['@app.relation.mapper'] arguments: ['@service.ldview', '@app.relation.mapper']
parent: service.loader.base
service.loader.ldraw:
class: AppBundle\Service\Loader\LDrawLoaderService
arguments: ['@service.ldview', '%ldraw_url%', '@manager.ldraw', '@util.dat.parser']
parent: service.loader
service.loader.relation: service.loader.relation:
class: AppBundle\Service\Loader\RelationLoader class: AppBundle\Service\Loader\RelationLoader
arguments: ['@manager.ldraw.model', '@repository.rebrickable.part', '@api.manager.rebrickable'] arguments: ['@api.manager.rebrickable', '@app.relation.mapper']
parent: service.loader parent: service.loader.base

View File

@ -1,28 +1 @@
services: services:
manager.ldraw.keyword:
class: AppBundle\Manager\LDraw\KeywordManager
arguments:
- "@repository.ldraw.keyword"
manager.ldraw.category:
class: AppBundle\Manager\LDraw\CategoryManager
arguments:
- "@repository.ldraw.category"
manager.ldraw.type:
class: AppBundle\Manager\LDraw\TypeManager
arguments:
- "@repository.ldraw.type"
manager.ldraw.subpart:
class: AppBundle\Manager\LDraw\SubpartManager
arguments:
- "@repository.ldraw.subpart"
manager.ldraw.model:
class: AppBundle\Manager\LDraw\ModelManager
arguments:
- "@repository.ldraw.model"
manager.ldraw.alias:
class: AppBundle\Manager\LDraw\AliasManager
arguments:
- "@repository.ldraw.alias"

View File

@ -41,6 +41,13 @@ services:
arguments: arguments:
- AppBundle\Entity\LDraw\Alias - AppBundle\Entity\LDraw\Alias
repository.ldraw.author:
class: Doctrine\ORM\EntityRepository
factory: ["@doctrine", getRepository]
arguments:
- AppBundle\Entity\LDraw\Author
repository.rebrickable.category: repository.rebrickable.category:
class: Doctrine\ORM\EntityRepository class: Doctrine\ORM\EntityRepository
factory: ["@doctrine", getRepository] factory: ["@doctrine", getRepository]

View File

@ -1,19 +1,10 @@
services: services:
manager.ldraw: app.twig_extension:
class: AppBundle\Manager\LDrawManager class: AppBundle\Twig\AppExtension
arguments: public: false
- '@manager.ldraw.category' arguments: ['@api.manager.rebrickable']
- '@manager.ldraw.keyword'
- '@manager.ldraw.type'
- '@manager.ldraw.subpart'
- '@manager.ldraw.model'
- '@manager.ldraw.alias'
app.form.filter_set:
class: AppBundle\Form\FilterSetType
arguments: ['@api.manager.brickset']
tags: tags:
- { name: form.type } - { name: twig.extension }
app.relation.mapper: app.relation.mapper:
class: AppBundle\Utils\RelationMapper class: AppBundle\Utils\RelationMapper

View File

@ -23,10 +23,11 @@
"sensio/framework-extra-bundle": "^3.0.2", "sensio/framework-extra-bundle": "^3.0.2",
"incenteev/composer-parameter-handler": "^2.0", "incenteev/composer-parameter-handler": "^2.0",
"guzzlehttp/guzzle": "^6.2", "guzzlehttp/guzzle": "^6.2",
"knplabs/knp-menu-bundle": "^2.0", "knplabs/knp-menu-bundle": "^2.1",
"oneup/flysystem-bundle": "^1.7", "oneup/flysystem-bundle": "^1.7",
"knplabs/knp-paginator-bundle": "^2.5", "knplabs/knp-paginator-bundle": "^2.5",
"lexik/form-filter-bundle": "~5.0" "lexik/form-filter-bundle": "~5.0",
"liip/imagine-bundle": "^1.7"
}, },
"require-dev": { "require-dev": {
"sensio/generator-bundle": "^3.0", "sensio/generator-bundle": "^3.0",

441
composer.lock generated
View File

@ -4,8 +4,67 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "6956e65252fd75b8d2e258fcc54fa3a9", "content-hash": "6a70240b303126447c6fc3c6f75bf6ff",
"packages": [ "packages": [
{
"name": "composer/ca-bundle",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "b17e6153cb7f33c7e44eb59578dc12eee5dc8e12"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/b17e6153cb7f33c7e44eb59578dc12eee5dc8e12",
"reference": "b17e6153cb7f33c7e44eb59578dc12eee5dc8e12",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.5",
"psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0"
},
"suggest": {
"symfony/process": "This is necessary to reliably check whether openssl_x509_parse is vulnerable on older php versions, but can be ignored on PHP 5.5.6+"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"time": "2017-03-06T11:59:08+00:00"
},
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
"version": "v1.2.7", "version": "v1.2.7",
@ -952,6 +1011,63 @@
], ],
"time": "2017-03-20T17:10:46+00:00" "time": "2017-03-20T17:10:46+00:00"
}, },
{
"name": "imagine/imagine",
"version": "v0.6.3",
"source": {
"type": "git",
"url": "https://github.com/avalanche123/Imagine.git",
"reference": "149041d2a1b517107bfe270ca2b1a17aa341715d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/avalanche123/Imagine/zipball/149041d2a1b517107bfe270ca2b1a17aa341715d",
"reference": "149041d2a1b517107bfe270ca2b1a17aa341715d",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"sami/sami": "dev-master"
},
"suggest": {
"ext-gd": "to use the GD implementation",
"ext-gmagick": "to use the Gmagick implementation",
"ext-imagick": "to use the Imagick implementation"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "0.7-dev"
}
},
"autoload": {
"psr-0": {
"Imagine": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bulat Shakirzyanov",
"email": "mallluhuct@gmail.com",
"homepage": "http://avalanche123.com"
}
],
"description": "Image processing for PHP 5.3",
"homepage": "http://imagine.readthedocs.org/",
"keywords": [
"drawing",
"graphics",
"image manipulation",
"image processing"
],
"time": "2015-09-19T16:54:05+00:00"
},
{ {
"name": "incenteev/composer-parameter-handler", "name": "incenteev/composer-parameter-handler",
"version": "v2.1.2", "version": "v2.1.2",
@ -1053,6 +1169,94 @@
], ],
"time": "2014-01-12T16:20:24+00:00" "time": "2014-01-12T16:20:24+00:00"
}, },
{
"name": "knplabs/gaufrette",
"version": "v0.3.1",
"source": {
"type": "git",
"url": "https://github.com/KnpLabs/Gaufrette.git",
"reference": "771ad16f4b2e7f9d35f44b201956e83c6fbf5dde"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/KnpLabs/Gaufrette/zipball/771ad16f4b2e7f9d35f44b201956e83c6fbf5dde",
"reference": "771ad16f4b2e7f9d35f44b201956e83c6fbf5dde",
"shasum": ""
},
"require": {
"php": ">=5.4"
},
"conflict": {
"microsoft/windowsazure": "<0.4.3"
},
"require-dev": {
"amazonwebservices/aws-sdk-for-php": "1.5.*",
"aws/aws-sdk-php": "^2.4.12",
"doctrine/dbal": ">=2.3",
"dropbox-php/dropbox-php": "*",
"google/apiclient": "~1.1.3",
"herzult/php-ssh": "*",
"league/flysystem": "~1.0",
"mikey179/vfsstream": "~1.2.0",
"phpseclib/phpseclib": "^2.0",
"phpspec/phpspec": "~2.4",
"phpunit/phpunit": "3.7.*",
"rackspace/php-opencloud": "^1.9.2"
},
"suggest": {
"amazonwebservices/aws-sdk-for-php": "to use the legacy Amazon S3 adapters",
"aws/aws-sdk-php": "to use the Amazon S3 adapter",
"doctrine/dbal": "to use the Doctrine DBAL adapter",
"dropbox-php/dropbox-php": "to use the Dropbox adapter",
"ext-apc": "to use the APC adapter",
"ext-curl": "*",
"ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters",
"ext-mbstring": "*",
"ext-mongo": "*",
"ext-zip": "to use the Zip adapter",
"google/apiclient": "to use GoogleCloudStorage adapter",
"herzult/php-ssh": "to use SFtp adapter",
"knplabs/knp-gaufrette-bundle": "to use with Symfony2",
"league/flysystem": "to use Flysystem adapters",
"microsoft/windowsazure": "to use Microsoft Azure Blob Storage adapter",
"phpseclib/phpseclib": "to use PhpseclibSftp adapter",
"rackspace/php-opencloud": "to use Opencloud adapter"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.4.x-dev"
}
},
"autoload": {
"psr-0": {
"Gaufrette": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "The contributors",
"homepage": "http://github.com/knplabs/Gaufrette/contributors"
},
{
"name": "KnpLabs Team",
"homepage": "http://knplabs.com"
}
],
"description": "PHP5 library that provides a filesystem abstraction layer",
"homepage": "http://knplabs.com",
"keywords": [
"abstraction",
"file",
"filesystem",
"media"
],
"time": "2017-03-20T01:23:34+00:00"
},
{ {
"name": "knplabs/knp-components", "name": "knplabs/knp-components",
"version": "1.3.4", "version": "1.3.4",
@ -1124,6 +1328,64 @@
], ],
"time": "2016-12-06T18:10:24+00:00" "time": "2016-12-06T18:10:24+00:00"
}, },
{
"name": "knplabs/knp-gaufrette-bundle",
"version": "v0.4.0",
"source": {
"type": "git",
"url": "https://github.com/KnpLabs/KnpGaufretteBundle.git",
"reference": "06d91a8a575773cd0361c1246c9c499b6bdd5d68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/KnpLabs/KnpGaufretteBundle/zipball/06d91a8a575773cd0361c1246c9c499b6bdd5d68",
"reference": "06d91a8a575773cd0361c1246c9c499b6bdd5d68",
"shasum": ""
},
"require": {
"knplabs/gaufrette": "~0.1.7|~0.2|~0.3",
"symfony/framework-bundle": "~2.0|~3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.2",
"symfony/console": "~2.0|~3.0",
"symfony/yaml": "~2.0|~3.0"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "0.4.x-dev"
}
},
"autoload": {
"psr-4": {
"Knp\\Bundle\\GaufretteBundle\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "The contributors",
"homepage": "https://github.com/knplabs/KnpGaufretteBundle/contributors"
},
{
"name": "Antoine Hérault",
"email": "antoine.herault@gmail.com"
}
],
"description": "Allows to easily use the Gaufrette library in a Symfony project",
"homepage": "http://knplabs.com",
"keywords": [
"abstraction",
"file",
"filesystem",
"media"
],
"time": "2017-03-16T21:01:25+00:00"
},
{ {
"name": "knplabs/knp-menu", "name": "knplabs/knp-menu",
"version": "2.2.0", "version": "2.2.0",
@ -1394,16 +1656,16 @@
}, },
{ {
"name": "lexik/form-filter-bundle", "name": "lexik/form-filter-bundle",
"version": "v5.0.3", "version": "v5.0.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/lexik/LexikFormFilterBundle.git", "url": "https://github.com/lexik/LexikFormFilterBundle.git",
"reference": "124a6c8e9eb109e7616a18d916bbc33137bb308d" "reference": "28c09d6d9f278875ca9648c4b66eeb457c37d3b6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/lexik/LexikFormFilterBundle/zipball/124a6c8e9eb109e7616a18d916bbc33137bb308d", "url": "https://api.github.com/repos/lexik/LexikFormFilterBundle/zipball/28c09d6d9f278875ca9648c4b66eeb457c37d3b6",
"reference": "124a6c8e9eb109e7616a18d916bbc33137bb308d", "reference": "28c09d6d9f278875ca9648c4b66eeb457c37d3b6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1444,13 +1706,113 @@
"description": "This bundle aim to provide classes to build some form filters and then build a doctrine query from this form filter.", "description": "This bundle aim to provide classes to build some form filters and then build a doctrine query from this form filter.",
"homepage": "https://github.com/lexik/LexikFormFilterBundle", "homepage": "https://github.com/lexik/LexikFormFilterBundle",
"keywords": [ "keywords": [
"Symfony2",
"bundle", "bundle",
"doctrine", "doctrine",
"filter", "filter",
"form" "form",
"symfony"
], ],
"time": "2017-01-24T13:03:45+00:00" "time": "2017-03-27T07:28:34+00:00"
},
{
"name": "liip/imagine-bundle",
"version": "1.7.4",
"source": {
"type": "git",
"url": "https://github.com/liip/LiipImagineBundle.git",
"reference": "105dd9c3446e3eb44e33161d4e636a3abafb6d7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/105dd9c3446e3eb44e33161d4e636a3abafb6d7f",
"reference": "105dd9c3446e3eb44e33161d4e636a3abafb6d7f",
"shasum": ""
},
"require": {
"imagine/imagine": "^0.6.3,<0.7",
"php": "^5.3.9|^7.0",
"symfony/asset": "~2.3|~3.0",
"symfony/filesystem": "~2.3|~3.0",
"symfony/finder": "~2.3|~3.0",
"symfony/framework-bundle": "~2.3|~3.0",
"symfony/options-resolver": "~2.3|~3.0",
"symfony/process": "~2.3|~3.0",
"symfony/templating": "~2.3|~3.0",
"symfony/translation": "~2.3|~3.0"
},
"require-dev": {
"amazonwebservices/aws-sdk-for-php": "~1.0",
"aws/aws-sdk-php": "~2.4",
"doctrine/cache": "~1.1",
"doctrine/orm": "~2.3",
"ext-gd": "*",
"friendsofphp/php-cs-fixer": "~2.0",
"phpunit/phpunit": "~4.3|~5.0",
"psr/log": "~1.0",
"satooshi/php-coveralls": "~1.0",
"sllh/php-cs-fixer-styleci-bridge": "~2.1",
"symfony/browser-kit": "~2.3|~3.0",
"symfony/console": "~2.3|~3.0",
"symfony/dependency-injection": "~2.3|~3.0",
"symfony/form": "~2.3|~3.0",
"symfony/phpunit-bridge": "~2.3|~3.0",
"symfony/validator": "~2.3|~3.0",
"symfony/yaml": "~2.3|~3.0",
"twig/twig": "~1.12|~2.0"
},
"suggest": {
"alcaeus/mongo-php-adapter": "required on PHP >= 7.0 to use mongo components with mongodb extension",
"amazonwebservices/aws-sdk-for-php": "required to use AWS version 1 cache resolver",
"aws/aws-sdk-php": "required to use AWS version 2/3 cache resolver",
"doctrine/mongodb-odm": "required to use mongodb-backed doctrine components",
"ext-exif": "required to read EXIF metadata from images",
"ext-gd": "required to use gd driver",
"ext-gmagick": "required to use gmagick driver",
"ext-imagick": "required to use imagick driver",
"ext-mongo": "required for mongodb components on PHP <7.0",
"ext-mongodb": "required for mongodb components on PHP >=7.0",
"league/flysystem": "required to use FlySystem data loader or cache resolver",
"monolog/monolog": "A psr/log compatible logger is required to enable logging",
"twig/twig": "required to use the provided Twig extension. Version 1.12 or greater needed"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-1.0": "1.7-dev"
}
},
"autoload": {
"psr-4": {
"Liip\\ImagineBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Liip and other contributors",
"homepage": "https://github.com/liip/LiipImagineBundle/contributors"
}
],
"description": "This bundle provides an image manipulation abstraction toolkit for Symfony-based projects.",
"homepage": "http://liip.ch",
"keywords": [
"bundle",
"image",
"imagine",
"liip",
"manipulation",
"photos",
"pictures",
"symfony",
"transformation"
],
"time": "2017-03-02T20:18:55+00:00"
}, },
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
@ -1532,16 +1894,16 @@
}, },
{ {
"name": "oneup/flysystem-bundle", "name": "oneup/flysystem-bundle",
"version": "1.11.0", "version": "1.12.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/1up-lab/OneupFlysystemBundle.git", "url": "https://github.com/1up-lab/OneupFlysystemBundle.git",
"reference": "a68f83415e3af2313c529be6b22bfddfcfe8e90f" "reference": "2addd1077360790a7722fef09388a003576d585c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/1up-lab/OneupFlysystemBundle/zipball/a68f83415e3af2313c529be6b22bfddfcfe8e90f", "url": "https://api.github.com/repos/1up-lab/OneupFlysystemBundle/zipball/2addd1077360790a7722fef09388a003576d585c",
"reference": "a68f83415e3af2313c529be6b22bfddfcfe8e90f", "reference": "2addd1077360790a7722fef09388a003576d585c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1562,6 +1924,7 @@
"league/flysystem-ziparchive": "~1.0", "league/flysystem-ziparchive": "~1.0",
"litipk/flysystem-fallback-adapter": "~0.1", "litipk/flysystem-fallback-adapter": "~0.1",
"phpunit/phpunit": "^4.4", "phpunit/phpunit": "^4.4",
"superbalist/flysystem-google-storage": "~4.0",
"symfony/asset": "~2.0|~3.0", "symfony/asset": "~2.0|~3.0",
"symfony/browser-kit": "~2.0|~3.0", "symfony/browser-kit": "~2.0|~3.0",
"symfony/finder": "~2.0|~3.0", "symfony/finder": "~2.0|~3.0",
@ -1583,6 +1946,7 @@
"league/flysystem-webdav": "Allows you to use WebDAV storage", "league/flysystem-webdav": "Allows you to use WebDAV storage",
"league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
"litipk/flysystem-fallback-adapter": "Allows you to use a fallback filesystem", "litipk/flysystem-fallback-adapter": "Allows you to use a fallback filesystem",
"superbalist/flysystem-google-storage": "Allows you to use Google Cloud Storage buckets",
"twistor/flysystem-stream-wrapper": "Allows you to use stream wrapper" "twistor/flysystem-stream-wrapper": "Allows you to use stream wrapper"
}, },
"type": "symfony-bundle", "type": "symfony-bundle",
@ -1611,7 +1975,7 @@
"abstraction", "abstraction",
"filesystem" "filesystem"
], ],
"time": "2017-03-03T13:41:53+00:00" "time": "2017-03-27T08:48:46+00:00"
}, },
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",
@ -1928,19 +2292,20 @@
}, },
{ {
"name": "sensiolabs/security-checker", "name": "sensiolabs/security-checker",
"version": "v4.0.2", "version": "v4.0.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sensiolabs/security-checker.git", "url": "https://github.com/sensiolabs/security-checker.git",
"reference": "56bded66985e22f6eac2cf86735fd21c625bff2f" "reference": "9e69eddf3bc49d1ee5c7908564da3141796d4bbc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/56bded66985e22f6eac2cf86735fd21c625bff2f", "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/9e69eddf3bc49d1ee5c7908564da3141796d4bbc",
"reference": "56bded66985e22f6eac2cf86735fd21c625bff2f", "reference": "9e69eddf3bc49d1ee5c7908564da3141796d4bbc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"composer/ca-bundle": "^1.0",
"symfony/console": "~2.7|~3.0" "symfony/console": "~2.7|~3.0"
}, },
"bin": [ "bin": [
@ -1968,7 +2333,7 @@
} }
], ],
"description": "A security checker for your composer.lock", "description": "A security checker for your composer.lock",
"time": "2017-03-09T17:33:20+00:00" "time": "2017-03-31T14:50:32+00:00"
}, },
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
@ -2482,16 +2847,16 @@
}, },
{ {
"name": "symfony/symfony", "name": "symfony/symfony",
"version": "v3.2.6", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/symfony.git", "url": "https://github.com/symfony/symfony.git",
"reference": "b0f8a7fa4b8baadf9db299cb6ac87c96a8977dbe" "reference": "1631040ea8fc5e0e405c00d35cbf2c0b7b555f64"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/symfony/zipball/b0f8a7fa4b8baadf9db299cb6ac87c96a8977dbe", "url": "https://api.github.com/repos/symfony/symfony/zipball/1631040ea8fc5e0e405c00d35cbf2c0b7b555f64",
"reference": "b0f8a7fa4b8baadf9db299cb6ac87c96a8977dbe", "reference": "1631040ea8fc5e0e405c00d35cbf2c0b7b555f64",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2622,7 +2987,7 @@
"keywords": [ "keywords": [
"framework" "framework"
], ],
"time": "2017-03-10T18:35:48+00:00" "time": "2017-04-05T12:52:29+00:00"
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
@ -2690,49 +3055,59 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v2.1.2", "version": "v2.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
"reference": "c7de769d7b44f2c9de68e1f678b65efd8126f60b" "reference": "d6f17423412d33df6b69c9aaf12037b91703533b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c7de769d7b44f2c9de68e1f678b65efd8126f60b", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/d6f17423412d33df6b69c9aaf12037b91703533b",
"reference": "c7de769d7b44f2c9de68e1f678b65efd8126f60b", "reference": "d6f17423412d33df6b69c9aaf12037b91703533b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/annotations": "^1.2",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"php": "^5.3.6 || >=7.0 <7.2", "php": "^5.3.6 || >=7.0 <7.2",
"sebastian/diff": "^1.1", "sebastian/diff": "^1.1",
"symfony/console": "^2.3 || ^3.0", "symfony/console": "^2.4 || ^3.0",
"symfony/event-dispatcher": "^2.1 || ^3.0", "symfony/event-dispatcher": "^2.1 || ^3.0",
"symfony/filesystem": "^2.4 || ^3.0", "symfony/filesystem": "^2.4 || ^3.0",
"symfony/finder": "^2.2 || ^3.0", "symfony/finder": "^2.2 || ^3.0",
"symfony/options-resolver": "^2.6 || ^3.0",
"symfony/polyfill-php54": "^1.0", "symfony/polyfill-php54": "^1.0",
"symfony/polyfill-php55": "^1.3", "symfony/polyfill-php55": "^1.3",
"symfony/polyfill-php70": "^1.0",
"symfony/polyfill-xml": "^1.3", "symfony/polyfill-xml": "^1.3",
"symfony/process": "^2.3 || ^3.0", "symfony/process": "^2.3 || ^3.0",
"symfony/stopwatch": "^2.5 || ^3.0" "symfony/stopwatch": "^2.5 || ^3.0"
}, },
"conflict": { "conflict": {
"hhvm": "<3.9" "hhvm": "<3.18"
}, },
"require-dev": { "require-dev": {
"gecko-packages/gecko-php-unit": "^2.0", "gecko-packages/gecko-php-unit": "^2.0",
"justinrainbow/json-schema": "^5.0", "justinrainbow/json-schema": "^5.0",
"phpunit/phpunit": "^4.5 || ^5.0", "phpunit/phpunit": "^4.5 || ^5.0",
"satooshi/php-coveralls": "^1.0", "satooshi/php-coveralls": "^1.0",
"symfony/phpunit-bridge": "^3.2" "symfony/phpunit-bridge": "^3.2.2"
}, },
"suggest": { "suggest": {
"ext-xml": "For better performance." "ext-mbstring": "For handling non-UTF8 characters in cache signature.",
"ext-xml": "For better performance.",
"symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible."
}, },
"bin": [ "bin": [
"php-cs-fixer" "php-cs-fixer"
], ],
"type": "application", "type": "application",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"PhpCsFixer\\": "src/" "PhpCsFixer\\": "src/"
@ -2753,7 +3128,7 @@
} }
], ],
"description": "A tool to automatically fix PHP code style", "description": "A tool to automatically fix PHP code style",
"time": "2017-03-15T17:13:07+00:00" "time": "2017-03-31T16:16:30+00:00"
}, },
{ {
"name": "ircmaxell/password-compat", "name": "ircmaxell/password-compat",
@ -2905,7 +3280,7 @@
}, },
{ {
"name": "symfony/phpunit-bridge", "name": "symfony/phpunit-bridge",
"version": "v3.2.6", "version": "v3.2.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git", "url": "https://github.com/symfony/phpunit-bridge.git",

View File

@ -4,6 +4,7 @@ var gulp = require('gulp'),
gulp.task('css', function() { gulp.task('css', function() {
return gulp.src([ return gulp.src([
'node_modules/semantic-ui/dist/semantic.css', 'node_modules/semantic-ui/dist/semantic.css',
'node_modules/lightbox2/dist/css/lightbox.css',
'app/Resources/assets/style/style.scss', 'app/Resources/assets/style/style.scss',
]) ])
.pipe(plugins.sass().on('error', plugins.sass.logError)) .pipe(plugins.sass().on('error', plugins.sass.logError))
@ -31,23 +32,35 @@ gulp.task('js', function() {
return gulp.src([ return gulp.src([
'node_modules/jquery/dist/jquery.js', 'node_modules/jquery/dist/jquery.js',
'node_modules/semantic-ui/dist/semantic.js', 'node_modules/semantic-ui/dist/semantic.js',
'node_modules/lightbox2/dist/js/lightbox.js',
'app/Resources/assets/js/**.js', 'app/Resources/assets/js/**.js',
'node_modules/three/examples/js/libs/stats.min.js'
]) ])
.pipe(plugins.concat('main.js')) .pipe(plugins.concat('main.js'))
.pipe(gulp.dest('web/resources/js')); .pipe(gulp.dest('web/resources/js'));
}); });
gulp.task('files', function () { gulp.task('files:semantic', function () {
return gulp.src('node_modules/semantic-ui/dist/themes/**') return gulp.src(
.pipe(plugins.newer('web/resources/css/themes')) 'node_modules/semantic-ui/dist/themes/**'
)
.pipe(gulp.dest('web/resources/css/themes')); .pipe(gulp.dest('web/resources/css/themes'));
}); });
gulp.task('files:images', function () {
return gulp.src([
'node_modules/lightbox2/dist/images/**',
'app/Resources/assets/images/**'
])
.pipe(gulp.dest('web/resources/images'));
});
gulp.task('watch', ['js', 'css', 'three'], function () { gulp.task('watch', ['js', 'css', 'three'], function () {
gulp.watch('app/Resources/assets/js/**.js' , ['js']); gulp.watch('app/Resources/assets/js/**.js' , ['js']);
gulp.watch('app/Resources/assets/style/**/*.sass' , ['css']); gulp.watch('app/Resources/assets/style/**/*.scss' , ['css']);
}); });
gulp.task('default', function () { gulp.task('default', function () {
return gulp.start(['files', 'js', 'css', 'three']); return gulp.start(['files:semantic', 'files:images', 'js', 'css', 'three']);
}); });

View File

@ -11,6 +11,7 @@
"gulp-sass": "^2.3.2", "gulp-sass": "^2.3.2",
"gulp-watch": "^4.3.11", "gulp-watch": "^4.3.11",
"jquery": "^3.2.1", "jquery": "^3.2.1",
"lightbox2": "^2.9.0",
"semantic-ui": "^2.2.9", "semantic-ui": "^2.2.9",
"three": "^0.84.0" "three": "^0.84.0"
}, },

View File

@ -13,7 +13,6 @@ use AppBundle\Api\Exception\ApiException;
use AppBundle\Api\Exception\AuthenticationFailedException; use AppBundle\Api\Exception\AuthenticationFailedException;
use AppBundle\Api\Exception\CallFailedException; use AppBundle\Api\Exception\CallFailedException;
use AppBundle\Api\Exception\EmptyResponseException; use AppBundle\Api\Exception\EmptyResponseException;
use Symfony\Component\Asset\Exception\LogicException;
use Symfony\Component\Debug\Exception\ContextErrorException; use Symfony\Component\Debug\Exception\ContextErrorException;
class Brickset extends \SoapClient class Brickset extends \SoapClient
@ -47,6 +46,7 @@ class Brickset extends \SoapClient
$this->apiKey = $apikey; $this->apiKey = $apikey;
$options['cache_wsdl'] = WSDL_CACHE_NONE; $options['cache_wsdl'] = WSDL_CACHE_NONE;
$options['exceptions'] = true;
foreach (self::$classmap as $key => $value) { foreach (self::$classmap as $key => $value) {
if (!isset($options['classmap'][$key])) { if (!isset($options['classmap'][$key])) {
@ -56,7 +56,12 @@ class Brickset extends \SoapClient
if (!$wsdl) { if (!$wsdl) {
$wsdl = self::WSDL; $wsdl = self::WSDL;
} }
parent::__construct($wsdl, $options);
try {
parent::__construct($wsdl, $options);
} catch (\Exception $exception) {
throw new ApiException(ApiException::BRICKSET);
}
} }
/** /**
@ -73,11 +78,14 @@ class Brickset extends \SoapClient
try { try {
$this->checkApiKey(); $this->checkApiKey();
return $this->__soapCall($method, [$parameters])->{$method.'Result'}; return $this->__soapCall($method, [$parameters])->{$method.'Result'};
} catch (\SoapFault $e) { } catch (\SoapFault $e) {
throw new CallFailedException(ApiException::BRICKSET); throw new CallFailedException(ApiException::BRICKSET);
} catch (ContextErrorException $e) { } catch (ContextErrorException $e) {
throw new EmptyResponseException(ApiException::BRICKSET); throw new EmptyResponseException(ApiException::BRICKSET);
} catch (\Exception $e) {
throw new ApiException(ApiException::BRICKSET);
} }
} }
@ -105,7 +113,7 @@ class Brickset extends \SoapClient
$response = $this->call('getSets', $parameters)->sets; $response = $this->call('getSets', $parameters)->sets;
return is_array($response) ? $response : [$response]; return is_array($response) ? $response : [$this->getSet($response->getSetID())];
} }
/** /**
@ -258,7 +266,7 @@ class Brickset extends \SoapClient
{ {
$parameters['apiKey'] = $this->apiKey; $parameters['apiKey'] = $this->apiKey;
if($this->__soapCall('checkKey', [$parameters])->checkKeyResult != 'OK') { if ($this->__soapCall('checkKey', [$parameters])->checkKeyResult != 'OK') {
throw new AuthenticationFailedException(ApiException::BRICKSET); throw new AuthenticationFailedException(ApiException::BRICKSET);
} }
} }

View File

@ -9,7 +9,6 @@ use AppBundle\Api\Exception\EmptyResponseException;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\ConnectException;
use Symfony\Component\Asset\Exception\LogicException;
class Rebrickable_v3 class Rebrickable_v3
{ {
@ -59,11 +58,11 @@ class Rebrickable_v3
} catch (ClientException $e) { } catch (ClientException $e) {
if ($e->getCode() == 404) { if ($e->getCode() == 404) {
throw new EmptyResponseException(ApiException::REBRICKABLE); throw new EmptyResponseException(ApiException::REBRICKABLE);
} else if ($e->getCode() == 401) { } elseif ($e->getCode() == 401) {
throw new AuthenticationFailedException(ApiException::REBRICKABLE); throw new AuthenticationFailedException(ApiException::REBRICKABLE);
} }
throw new ApiException(ApiException::REBRICKABLE,$e,$e->getCode()); throw new ApiException(ApiException::REBRICKABLE, $e, $e->getCode());
} }
} }
} }

View File

@ -47,4 +47,19 @@ class BricksetManager
return isset($sets[0]) ? $sets[0] : null; return isset($sets[0]) ? $sets[0] : null;
} }
public function getSetInstructions($id)
{
return $this->bricksetClient->getInstructions($id);
}
public function getSetReviews($id)
{
return $this->bricksetClient->getReviews($id);
}
public function getAdditionalImages($id)
{
return $this->bricksetClient->getAdditionalImages($id);
}
} }

View File

@ -119,18 +119,19 @@ class RebrickableManager
return $this->serializer->deserialize($data, PartCategory::class, self::FORMAT); return $this->serializer->deserialize($data, PartCategory::class, self::FORMAT);
} }
public function getPartsByLDrawNumber($number) { public function getPartsByLDrawNumber($number)
{
$options = [ $options = [
'query' => [ 'query' => [
'ldraw_id' => $number 'ldraw_id' => $number,
], ],
]; ];
$response = $this->rebrickableClient->call('GET','lego/parts', $options); $response = $this->rebrickableClient->call('GET', 'lego/parts', $options);
$data = json_decode($response, true)['results']; $data = json_decode($response, true)['results'];
return $this->serializer->denormalize($data,Part::class.'[]',self::FORMAT); return $this->serializer->denormalize($data, Part::class.'[]', self::FORMAT);
} }
/** /**

View File

@ -1,39 +0,0 @@
<?php
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class LoadLDRawLibraryCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('app:load:ldraw')
->setDescription('Loads LDraw library parts')
->setHelp('This command allows you to..')
->addArgument('ldraw_path', InputArgument::OPTIONAL, 'Path to LDraw library folder');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$ldrawLoader = $this->getContainer()->get('service.loader.ldraw');
$ldrawLoader->setOutput($output);
//TODO log errors
try {
// TODO handle relative path to dir
if (($ldrawPath = $input->getArgument('ldraw_path')) == null) {
$ldrawPath = $ldrawLoader->downloadLibrary();
}
$ldrawLoader->loadData($ldrawPath);
} catch (\Exception $e) {
printf($e->getMessage());
}
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class LoadModelsCommand extends ContainerAwareCommand
{
use LockableTrait;
protected function configure()
{
$this
->setName('app:load:models')
->setDescription('Loads LDraw library models into database')
->setHelp('This command allows you to load LDraw library models into database while converting .dat files to .stl format.')
->setDefinition(
new InputDefinition([
new InputArgument('ldraw', InputArgument::REQUIRED, 'Path to LDraw library directory'),
// new InputOption('images', 'i', InputOption::VALUE_NONE, 'Do you want to generate images of models?'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Load all models from LDraw libary folder (/parts directory)'),
new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'Load single modle into database'),
new InputOption('update', 'u', InputOption::VALUE_NONE, 'Overwrite already loaded models'),
])
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$this->lock()) {
$output->writeln('The command is already running in another process.');
return 0;
}
$modelLoader = $this->getContainer()->get('service.loader.model');
$modelLoader->setOutput($output);
$modelLoader->setRewite($input->getOption('update'));
$ldraw = $input->getArgument('ldraw');
if ($ldrawPath = realpath($ldraw)) {
$modelLoader->setLDrawLibraryContext($ldrawPath);
if (($path = $input->getOption('file')) != null) {
if ($file = realpath($path)) {
$output->writeln([
'Loading model',
'path: '.$file,
'------------------------------------------------------------------------------',
]);
$modelLoader->loadOneModel($file);
$errorCount = $this->getContainer()->get('monolog.logger.loader')->countErrors();
$errors = $errorCount ? '<error>'.$errorCount.'</error>' : '<info>0</info>';
$output->writeln(['Done with "'.$errors.'" errors.']);
} else {
$output->writeln("File $path not found");
}
}
// Load all models inside ldraw/parts directory
if ($input->getOption('all')) {
$output->writeln([
'Loading models from LDraw library: <comment>'.$ldrawPath.'</comment>',
]);
$modelLoader->loadAllModels();
$errorCount = $this->getContainer()->get('monolog.logger.loader')->countErrors();
$errors = $errorCount ? '<error>'.$errorCount.'</error>' : '<info>0</info>';
$output->writeln(['Done with "'.$errors.'" errors.']);
}
} else {
$output->writeln($ldraw.' is not valid path');
}
$this->release();
}
}

View File

@ -12,8 +12,8 @@ class LoadRebrickableDataCommand extends ContainerAwareCommand
{ {
$this $this
->setName('app:load:rebrickable') ->setName('app:load:rebrickable')
->setDescription('Loads Rebrickable database') ->setDescription('Loads Rebrickable data about sets and parts into database.')
->setHelp('This command allows you to..'); ->setHelp('This command allows you to load Rebrickable CSV files containing information about sets and parts into database.');
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)

View File

@ -12,8 +12,8 @@ class LoadRelationCommand extends ContainerAwareCommand
{ {
$this $this
->setName('app:load:relations') ->setName('app:load:relations')
->setDescription('Loads relations between LDraw models and Rebrickable parts') ->setDescription('Loads relations between LDraw models and Rebrickable parts.')
->setHelp('This command allows you to..'); ->setHelp('This command allows you to load relation between models and parts into database.');
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)

View File

@ -1,10 +1,8 @@
<?php <?php
namespace AppBundle\Controller\Rebrickable; namespace AppBundle\Controller\Brickset;
use AppBundle\Entity\Rebrickable\Color; use AppBundle\Api\Exception\EmptyResponseException;
use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Entity\Rebrickable\Theme; use AppBundle\Entity\Rebrickable\Theme;
use AppBundle\Form\FilterSetType; use AppBundle\Form\FilterSetType;
@ -13,12 +11,12 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
/** /**
* @Route("/brickset") * @Route("/brickset/sets")
*/ */
class SetController extends Controller class SetController extends Controller
{ {
/** /**
* @Route("/", name="set_browse") * @Route("/", name="brickset_browse")
*/ */
public function browseAction(Request $request) public function browseAction(Request $request)
{ {
@ -30,11 +28,17 @@ class SetController extends Controller
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData(); $data = $form->getData();
$sets = $this->get('api.client.brickset')->getSets([ try {
'theme' => $data['theme'] ? $data['theme']->getTheme() : '', $sets = $this->get('api.client.brickset')->getSets([
'subtheme' => $data['subtheme'] ? $data['subtheme']->getSubtheme() : '', 'theme' => $data['theme'] ? $data['theme']->getTheme() : '',
'year' => $data['years'] ? $data['years']->getYear() : '', 'subtheme' => $data['subtheme'] ? $data['subtheme']->getSubtheme() : '',
]); 'year' => $data['years'] ? $data['years']->getYear() : '',
]);
} catch (EmptyResponseException $e) {
$this->addFlash('warning', 'No set found on '.$e->getService());
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
}
} }
return $this->render('brickset/browse.html.twig', [ return $this->render('brickset/browse.html.twig', [
@ -42,4 +46,61 @@ class SetController extends Controller
'sets' => $sets, 'sets' => $sets,
]); ]);
} }
/**
* @Route("/{id}/instructions", name="brickset_instructions")
*/
public function instructionsAction(Request $request, $id)
{
$instructions = [];
try {
$instructions = $this->get('api.manager.brickset')->getSetInstructions($id);
} catch (EmptyResponseException $e) {
// $this->addFlash('warning', 'No instruction found on Brickset.com');
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
}
return $this->render('brickset/instructions.html.twig', [
'instructions' => $instructions,
]);
}
/**
* @Route("/{id}/reviews", name="brickset_reviews")
*/
public function reviewsAction(Request $request, $id)
{
$reviews = [];
try {
$reviews = $this->get('api.manager.brickset')->getSetReviews($id);
} catch (EmptyResponseException $e) {
// $this->addFlash('warning', 'No review found on Brickset.com');
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
}
return $this->render('brickset/reviews.html.twig', [
'reviews' => $reviews,
]);
}
/**
* @Route("/{id}/images", name="brickset_images")
*/
public function imagesAction(Request $request, $id)
{
$images = [];
try {
$images = $this->get('api.manager.brickset')->getAdditionalImages($id);
} catch (EmptyResponseException $e) {
// $this->addFlash('warning', 'No images found on Brickset.com');
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
}
return $this->render('brickset/images.html.twig', [
'images' => $images,
]);
}
} }

View File

@ -2,7 +2,6 @@
namespace AppBundle\Controller; namespace AppBundle\Controller;
use AppBundle\Entity\LDraw\Model;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
@ -11,59 +10,33 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
/** /**
* @Route("/media") * @Route("/files")
*/ */
class MediaController extends Controller class MediaController extends Controller
{ {
/** /**
* @Route("/model/stl/{number}", name="model_stl") * @Route("/{path}", name="media_file", requirements={"path"=".+"})
* *
* @return Response * @return Response
*/ */
public function stlAction(Model $model) public function stlAction($path)
{ {
$mediaFilesystem = $this->get('oneup_flysystem.media_filesystem'); $mediaFilesystem = $this->get('oneup_flysystem.media_filesystem');
if ($mediaFilesystem->has($model->getPath())) { if ($mediaFilesystem->has($path)) {
$response = new BinaryFileResponse($mediaFilesystem->getAdapter()->getPathPrefix().DIRECTORY_SEPARATOR.$model->getPath()); $response = new BinaryFileResponse($mediaFilesystem->getAdapter()->getPathPrefix().DIRECTORY_SEPARATOR.$path);
$response->headers->set('Content-Type', 'application/vnd.ms-pki.stl'); $response->headers->set('Content-Type', $mediaFilesystem->getMimetype($path));
// Create the disposition of the file // Create the disposition of the file
$disposition = $response->headers->makeDisposition( $disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT, ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$model->getNumber().'.stl' basename($path)
); );
$response->headers->set('Content-Disposition', $disposition); $response->headers->set('Content-Disposition', $disposition);
return $response; return $response;
} }
throw new FileNotFoundException($model->getPath()); throw new FileNotFoundException($path);
}
/**
* @Route("/model/image/{number}", name="model_image")
*
* @return Response
*/
public function imageAction(Model $model)
{
$mediaFilesystem = $this->get('oneup_flysystem.media_filesystem');
if ($mediaFilesystem->has('ldraw'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$model->getNumber().'.png')) {
$response = new BinaryFileResponse($mediaFilesystem->getAdapter()->getPathPrefix().DIRECTORY_SEPARATOR.'ldraw'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$model->getNumber().'.png');
$response->headers->set('Content-Type', 'image/png');
// Create the disposition of the file
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$model->getNumber().'png'
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
}
throw new FileNotFoundException($model->getNumber().'png');
} }
} }

View File

@ -1,11 +1,11 @@
<?php <?php
namespace AppBundle\Controller\LDraw; namespace AppBundle\Controller;
use AppBundle\Entity\LDraw\Model; use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Rebrickable\Part; use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Form\Filter\ModelFilterType; use AppBundle\Form\Filter\Model\ModelFilterType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
@ -14,24 +14,21 @@ use Symfony\Component\HttpFoundation\Request;
/** /**
* Part controller. * Part controller.
* *
* @Route("ldraw") * @Route("models")
*/ */
class ModelController extends Controller class ModelController extends Controller
{ {
/** /**
* Lists all part entities. * Lists all part entities.
* *
* @Route("/models/", name="ldraw_model_index") * @Route("/", name="model_index")
* @Method("GET") * @Method("GET")
*/ */
public function indexAction(Request $request) public function indexAction(Request $request)
{ {
$em = $this->getDoctrine()->getManager();
$form = $this->get('form.factory')->create(ModelFilterType::class); $form = $this->get('form.factory')->create(ModelFilterType::class);
$filterBuilder = $this->get('repository.ldraw.model') $filterBuilder = $this->get('repository.ldraw.model')->getFilteredQueryBuilder();
->createQueryBuilder('model');
if ($request->query->has($form->getName())) { if ($request->query->has($form->getName())) {
// manually bind values from the request // manually bind values from the request
@ -45,10 +42,10 @@ class ModelController extends Controller
$models = $paginator->paginate( $models = $paginator->paginate(
$filterBuilder->getQuery(), $filterBuilder->getQuery(),
$request->query->getInt('page', 1)/*page number*/, $request->query->getInt('page', 1)/*page number*/,
$request->query->getInt('limit', 100)/*limit per page*/ $request->query->getInt('limit', 40)/*limit per page*/
); );
return $this->render('ldraw/model/index.html.twig', [ return $this->render('model/index.html.twig', [
'models' => $models, 'models' => $models,
'form' => $form->createView(), 'form' => $form->createView(),
]); ]);
@ -57,27 +54,29 @@ class ModelController extends Controller
/** /**
* Finds and displays a part entity. * Finds and displays a part entity.
* *
* @Route("/models/{number}", name="model_detail") * @Route("/{number}", name="model_detail")
* @Method("GET") * @Method("GET")
*/ */
public function detailAction($number) public function detailAction($number)
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
if($model = $this->get('manager.ldraw.model')->findByNumber($number)) { if ($model = $this->get('repository.ldraw.model')->findOneByNumber($number)) {
try { try {
$rbParts = $model != null ? $em->getRepository(Part::class)->findAllByModel($model) : null; $rbParts = $model != null ? $em->getRepository(Part::class)->findAllByModel($model) : null;
$sets = $model != null ? $em->getRepository(Set::class)->findAllByModel($model) : null; $sets = $model != null ? $em->getRepository(Set::class)->findAllByModel($model) : null;
return $this->render('ldraw/model/detail.html.twig', [ $related = $em->getRepository(Model::class)->findAllRelatedModels($model->getNumber());
return $this->render('model/detail.html.twig', [
'model' => $model, 'model' => $model,
'rbParts' => $rbParts, 'rbParts' => $rbParts,
'sets' => $sets, 'sets' => $sets,
'related' => $related,
]); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->addFlash('error', $e->getMessage()); $this->addFlash('error', $e->getMessage());
} }
} }
return $this->render('error/error.html.twig'); return $this->render('error/error.html.twig');

View File

@ -3,8 +3,8 @@
namespace AppBundle\Controller\Rebrickable; namespace AppBundle\Controller\Rebrickable;
use AppBundle\Entity\Rebrickable\Color; use AppBundle\Entity\Rebrickable\Color;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/** /**
* Part controller. * Part controller.
@ -14,10 +14,10 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class ColorController extends Controller class ColorController extends Controller
{ {
/** /**
*
* @Route("/", name="color_index") * @Route("/", name="color_index")
*/ */
public function indexAction() { public function indexAction()
{
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$colors = $em->getRepository(Color::class)->findAll(); $colors = $em->getRepository(Color::class)->findAll();
@ -26,4 +26,4 @@ class ColorController extends Controller
'colors' => $colors, 'colors' => $colors,
]); ]);
} }
} }

View File

@ -3,11 +3,13 @@
namespace AppBundle\Controller\Rebrickable; namespace AppBundle\Controller\Rebrickable;
use AppBundle\Api\Exception\EmptyResponseException; use AppBundle\Api\Exception\EmptyResponseException;
use AppBundle\Entity\Rebrickable\Category;
use AppBundle\Entity\Rebrickable\Part; use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
/** /**
* Part controller. * Part controller.
@ -16,19 +18,41 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
*/ */
class PartController extends Controller class PartController extends Controller
{ {
/**
* @Route("/", name="part_index")
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$queryBuilder = $em->getRepository(Part::class)->createQueryBuilder('part');
$queryBuilder->where('part.category = 17');
$paginator = $this->get('knp_paginator');
$sets = $paginator->paginate(
$queryBuilder->getQuery(),
$request->query->getInt('page', 1)/*page number*/,
$request->query->getInt('limit', 30)/*limit per page*/
);
return $this->render(':rebrickable/part:index.html.twig', [
'parts' => $sets,
]);
}
/** /**
* Finds and displays a part entity. * Finds and displays a part entity.
* *
* @Route("/{number}", name="rebrickable_part_show") * @Route("/{number}", name="reb_part_detail")
* @Method("GET") * @Method("GET")
*/ */
public function showAction(Part $part = null) public function detailAction(Part $part)
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$apiPart = null; $apiPart = null;
if($part) { if ($part) {
try { try {
$apiPart = $this->get('api.manager.rebrickable')->getPart($part->getNumber()); $apiPart = $this->get('api.manager.rebrickable')->getPart($part->getNumber());
} catch (EmptyResponseException $e) { } catch (EmptyResponseException $e) {

View File

@ -2,17 +2,13 @@
namespace AppBundle\Controller\Rebrickable; namespace AppBundle\Controller\Rebrickable;
use AppBundle\Api\Exception\EmptyResponseException;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Rebrickable\Color; use AppBundle\Entity\Rebrickable\Color;
use AppBundle\Entity\Rebrickable\Inventory_Part; use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Inventory_Set;
use AppBundle\Entity\Rebrickable\Part; use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Entity\Rebrickable\Theme;
use AppBundle\Form\FilterSetType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
/** /**
@ -21,87 +17,46 @@ use Symfony\Component\HttpFoundation\Response;
class SetController extends Controller class SetController extends Controller
{ {
/** /**
* @Route("/", name="set_index") * @Route("/{number}/parts", name="rebrickable_set_parts")
*/ */
public function indexAction(Request $request) public function partsAction(Set $set)
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$qb = $em->getRepository(Set::class)->createQueryBuilder('s');
$paginator = $this->get('knp_paginator');
$sets = $paginator->paginate(
$qb->getQuery(),
$request->query->getInt('page', 1)/*page number*/,
$request->query->getInt('limit', 30)/*limit per page*/
);
return $this->render('rebrickable/set/index.html.twig', [
'sets' => $sets,
]);
}
/**
* @Route("/detail/{number}_{name}", name="set_detail")
*/
public function detailAction(Request $request, $number, $name = null)
{
$brset = null;
try {
$brset = $this->get('api.manager.brickset')->getSetByNumber($number);
} catch (EmptyResponseException $e) {
$this->addFlash('warning', 'Set not found in Brickset database');
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
}
$set = $this->getDoctrine()->getManager()->getRepository(Set::class)->find($number);
$rbset = $this->get('api.manager.rebrickable')->getSet($number);
$em = $this->getDoctrine()->getManager();
$em->getRepository(Color::class)->findAll(); $em->getRepository(Color::class)->findAll();
$em->getRepository(Theme::class)->findAll(); $em->getRepository(Part::class)->findAllBySetNumber($set->getNumber());
return $this->render('rebrickable/set/detail.html.twig', [ $regularParts = $em->getRepository(Inventory_Part::class)->findAllRegularBySetNumber($set->getNumber());
'set' => $set, $spareParts = $em->getRepository(Inventory_Part::class)->findAllSpareBySetNumber($set->getNumber());
'brset' => $brset,
'rbset' => $rbset, $template = $this->render('rebrickable/set/parts.html.twig', [
'parts' => $em->getRepository(Model::class)->findAllBySetNumber($number), 'regularParts' => $regularParts,
'inventoryParts' => $em->getRepository(Inventory_Part::class)->findAllBySetNumber($number), 'spareParts' => $spareParts,
]); ]);
$json = json_encode($template->getContent());
$response = new Response($json, 200);
$response->headers->set('Content-Type', 'application/json');
return $response;
} }
/** /**
* @Route("/download/{number}", name="set_download") * @Route("/{number}/sets", name="rebrickable_set_sets")
*/ */
public function downloadZipAction(Request $request, $number) { public function setsAction(Set $set)
{
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$inventoryParts = $em->getRepository(Inventory_Part::class)->findAllBySetNumber($number); $inventorySets = $em->getRepository(Inventory_Set::class)->findAllBySetNumber($set->getNumber());
$zip = new \ZipArchive(); $template = $this->render('rebrickable/set/sets.html.twig', [
$zipName = 'set_'.$number.'.zip'; 'inventorySets' => $inventorySets,
$zip->open($zipName, \ZipArchive::CREATE); ]);
/** @var Inventory_Part $part */
foreach ($inventoryParts as $part) {
$filename = $part->getPart()->getNumber().'_('.$part->getColor()->getName().'_'.$part->getQuantity().'x).stl';
try { $json = json_encode($template->getContent());
if($part->getPart()->getModel()) { $response = new Response($json, 200);
$zip->addFromString($filename, $this->get('oneup_flysystem.media_filesystem')->read($part->getPart()->getModel()->getPath())); $response->headers->set('Content-Type', 'application/json');
}
} catch (\Exception $e) {
dump($e);
}
}
$zip->close();
$response = new Response(file_get_contents($zipName));
$response->headers->set('Content-Type', 'application/zip');
$response->headers->set('Content-Disposition', 'attachment;filename="' . $zipName . '"');
$response->headers->set('Content-length', filesize($zipName));
return $response; return $response;
} }

View File

@ -0,0 +1,82 @@
<?php
namespace AppBundle\Controller;
use AppBundle\Api\Exception\ApiException;
use AppBundle\Api\Exception\EmptyResponseException;
use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Form\Filter\Set\SetFilterType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* @Route("/sets")
*/
class SetController extends Controller
{
/**
* @Route("/", name="set_index")
*/
public function indexAction(Request $request)
{
$form = $this->get('form.factory')->create(SetFilterType::class);
$filterBuilder = $this->get('repository.rebrickable.set')
->createQueryBuilder('s');
if ($request->query->has($form->getName())) {
// manually bind values from the request
$form->submit($request->query->get($form->getName()));
// build the query from the given form object
$this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
}
$paginator = $this->get('knp_paginator');
$sets = $paginator->paginate(
$filterBuilder->getQuery(),
$request->query->getInt('page', 1)/*page number*/,
$request->query->getInt('limit', 30)/*limit per page*/
);
return $this->render('set/index.html.twig', [
'sets' => $sets,
'form' => $form->createView(),
]);
}
/**
* @Route("/{number}", name="set_detail")
*/
public function detailAction(Request $request, $number)
{
$rebrickableSet = null;
$bricksetSet = null;
try {
if (($rebrickableSet = $this->getDoctrine()->getManager()->getRepository(Set::class)->find($number)) == null) {
$this->addFlash('warning', 'Set not found in Rebrickable database');
}
$bricksetSet = $this->get('api.manager.brickset')->getSetByNumber($number);
dump($bricksetSet);
} catch (EmptyResponseException $e) {
$this->addFlash('warning', 'Set not found in Brickset database');
} catch (ApiException $e) {
$this->addFlash('error', $e->getService());
} catch (\Exception $e) {
$this->addFlash('error', $e->getMessage());
}
if (!$rebrickableSet && !$bricksetSet) {
return $this->render('error/error.html.twig');
}
return $this->render('set/detail.html.twig', [
'rbset' => $rebrickableSet,
'brset' => $bricksetSet,
]);
}
}

View File

@ -18,7 +18,7 @@ class Alias
/** /**
* @var Model * @var Model
* *
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Model", inversedBy="aliases", cascade={"persist"}) * @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Model", inversedBy="aliases")
*/ */
private $model; private $model;

View File

@ -0,0 +1,34 @@
<?php
namespace AppBundle\Entity\LDraw;
use AppBundle\Entity\Traits\IdentityTrait;
use AppBundle\Entity\Traits\UniqueNameTrait;
use Doctrine\ORM\Mapping as ORM;
/**
* Author.
*
* @ORM\Table(name="ldraw_author")
* @ORM\Entity(repositoryClass="AppBundle\Repository\LDraw\AuthorRepository")
*/
class Author
{
use IdentityTrait;
use UniqueNameTrait;
/**
* @var Author
*
* @ORM\OneToMany(targetEntity="AppBundle\Entity\LDraw\Model", mappedBy="author")
*/
private $models;
/**
* @return Author
*/
public function getModels()
{
return $this->models;
}
}

View File

@ -3,7 +3,7 @@
namespace AppBundle\Entity\LDraw; namespace AppBundle\Entity\LDraw;
use AppBundle\Entity\Traits\IdentityTrait; use AppBundle\Entity\Traits\IdentityTrait;
use AppBundle\Entity\Traits\NameTrait; use AppBundle\Entity\Traits\UniqueNameTrait;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
@ -17,7 +17,7 @@ use Doctrine\ORM\Mapping as ORM;
class Category class Category
{ {
use IdentityTrait; use IdentityTrait;
use NameTrait; use UniqueNameTrait;
/** /**
* @var Collection * @var Collection

View File

@ -3,7 +3,7 @@
namespace AppBundle\Entity\LDraw; namespace AppBundle\Entity\LDraw;
use AppBundle\Entity\Traits\IdentityTrait; use AppBundle\Entity\Traits\IdentityTrait;
use AppBundle\Entity\Traits\NameTrait; use AppBundle\Entity\Traits\UniqueNameTrait;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
@ -16,7 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
class Keyword class Keyword
{ {
use IdentityTrait; use IdentityTrait;
use NameTrait; use UniqueNameTrait;
/** /**
* @var ArrayCollection * @var ArrayCollection

View File

@ -17,13 +17,6 @@ class Model
{ {
use NumberTrait; use NumberTrait;
/**
* @var Type
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Type", inversedBy="models", cascade={"persist"})
*/
private $type;
/** /**
* @var string * @var string
* *
@ -48,7 +41,7 @@ class Model
/** /**
* @var ArrayCollection * @var ArrayCollection
* *
* @ORM\OneToMany(targetEntity="AppBundle\Entity\LDraw\Subpart", mappedBy="parent") * @ORM\OneToMany(targetEntity="AppBundle\Entity\LDraw\Subpart", mappedBy="parent", cascade={"persist"})
*/ */
private $subparts; private $subparts;
@ -74,9 +67,9 @@ class Model
private $path; private $path;
/** /**
* @var string * @var Author
* *
* @ORM\Column(type="string", length=255, nullable=true) * @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Author", cascade={"persist"}, inversedBy="models")
*/ */
private $author; private $author;
@ -122,7 +115,7 @@ class Model
/** /**
* Set author. * Set author.
* *
* @param string $author * @param Author $author
* *
* @return Model * @return Model
*/ */
@ -136,7 +129,7 @@ class Model
/** /**
* Get author. * Get author.
* *
* @return string * @return Author
*/ */
public function getAuthor() public function getAuthor()
{ {
@ -163,22 +156,6 @@ class Model
return $this; return $this;
} }
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* @param string $type
*/
public function setType($type)
{
$this->type = $type;
}
/** /**
* @return string * @return string
*/ */
@ -292,6 +269,20 @@ class Model
return $this; return $this;
} }
/**
* @param Subpart $subpart
*
* @return $this
*/
public function addSubpart($subpart)
{
if (!$this->subparts->contains($subpart)) {
$this->subparts->add($subpart);
}
return $this;
}
/** /**
* @return ArrayCollection * @return ArrayCollection
*/ */

View File

@ -16,7 +16,7 @@ class Subpart
* @var Model * @var Model
* *
* @ORM\Id * @ORM\Id
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Model", inversedBy="subparts", cascade={"persist"}) * @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Model", inversedBy="subparts")
*/ */
private $parent; private $parent;

View File

@ -1,91 +0,0 @@
<?php
namespace AppBundle\Entity\LDraw;
use AppBundle\Entity\Traits\IdentityTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Type.
*
* @ORM\Table(name="ldraw_type")
* @ORM\Entity(repositoryClass="AppBundle\Repository\LDraw\TypeRepository")
*/
class Type
{
use IdentityTrait;
/**
* @var string
*
* @ORM\Column(type="string", length=60, unique=true)
*/
private $name;
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity="Model", mappedBy="type")
*/
private $models;
/**
* BuildingKit constructor.
*/
public function __construct()
{
$this->models = new ArrayCollection();
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get models.
*
* @return ArrayCollection
*/
public function getModels()
{
return $this->models;
}
/**
* @param Model $model
*
* @return Type
*/
public function addModel(Model $model)
{
$this->models->add($model);
return $this;
}
/**
* @param Model $model
*
* @return Type
*/
public function removeModel(Model $model)
{
$this->models->remove($model);
return $this;
}
}

View File

@ -38,9 +38,17 @@ class Inventory
*/ */
protected $inventoryParts; protected $inventoryParts;
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity="AppBundle\Entity\Rebrickable\Inventory_Set", mappedBy="inventory")
*/
protected $inventorySets;
public function __construct() public function __construct()
{ {
$this->inventoryParts = new ArrayCollection(); $this->inventoryParts = new ArrayCollection();
$this->inventorySets = new ArrayCollection();
} }
/** /**
@ -90,4 +98,20 @@ class Inventory
{ {
$this->inventoryParts = $inventoryParts; $this->inventoryParts = $inventoryParts;
} }
/**
* @return Collection
*/
public function getInventorySets()
{
return $this->inventorySets;
}
/**
* @param Collection $inventorySets
*/
public function setInventorySets($inventorySets)
{
$this->inventorySets = $inventorySets;
}
} }

View File

@ -28,7 +28,7 @@ class Inventory_Part
/** /**
* @var bool * @var bool
* * @ORM\Id
* @ORM\Column(type="boolean") * @ORM\Column(type="boolean")
*/ */
protected $spare; protected $spare;

View File

@ -0,0 +1,81 @@
<?php
namespace AppBundle\Entity\Rebrickable;
use Doctrine\ORM\Mapping as ORM;
/**
* Inventory_Set.
*
* @ORM\Table(name="rebrickable_inventory_sets")
* @ORM\Entity(repositoryClass="AppBundle\Repository\Rebrickable\Inventory_SetRepository")
*/
class Inventory_Set
{
/**
* @var Inventory
* @ORM\Id
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Rebrickable\Inventory", inversedBy="inventorySets")
*/
protected $inventory;
/**
* @var Set
* @ORM\Id
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Rebrickable\Set", inversedBy="inventorySets")
*/
protected $set;
/**
* @var int
*
* @ORM\Column(type="integer")
*/
protected $quantity;
/**
* Get count.
*
* @return int
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* @return Set
*/
public function getSet()
{
return $this->set;
}
/**
* @param Set $set
*/
public function setSet($set)
{
$this->set = $set;
}
/**
* @return Inventory
*/
public function getInventory()
{
return $this->inventory;
}
/**
* @param Inventory $inventory
*
* @return Inventory_Set
*/
public function setInventory(Inventory $inventory)
{
$this->inventory = $inventory;
return $this;
}
}

View File

@ -47,12 +47,20 @@ class Set
*/ */
protected $theme; protected $theme;
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity="AppBundle\Entity\Rebrickable\Inventory_Set", mappedBy="set")
*/
protected $inventorySets;
/** /**
* Set constructor. * Set constructor.
*/ */
public function __construct() public function __construct()
{ {
$this->inventories = new ArrayCollection(); $this->inventories = new ArrayCollection();
$this->inventorySets = new ArrayCollection();
} }
/** /**
@ -133,6 +141,22 @@ class Set
return $this; return $this;
} }
/**
* @return Collection
*/
public function getInventorySets()
{
return $this->inventorySets;
}
/**
* @param Collection $inventorySets
*/
public function setInventorySets($inventorySets)
{
$this->inventorySets = $inventorySets;
}
/** /**
* @return Theme * @return Theme
*/ */

View File

@ -0,0 +1,33 @@
<?php
namespace AppBundle\Entity\Traits;
trait UniqueNameTrait
{
/**
* @var string
*
* @ORM\Column(type="string", length=255, nullable=true, unique=true)
*/
protected $name;
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*
* @return mixed
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace AppBundle\Exception;
use Exception;
class ConvertingFailedException extends \Exception
{
private $filepath;
public function __construct($form = '', $to = '', $message = '', $code = 0, Exception $previous = null)
{
$message = sprintf('Error converting "%s" file to "%s".', $form, $to);
parent::__construct($message, $code, $previous);
$this->filepath = $form;
}
/**
* @return mixed
*/
public function getFilepath()
{
return $this->filepath;
}
/**
* @param mixed $filepath
*/
public function setFilepath($filepath)
{
$this->filepath = $filepath;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace AppBundle\Exception;
use Throwable;
class ErrorParsingLineException extends FileException
{
public function __construct($path, $line, $message = '', $code = 0, Throwable $previous = null)
{
$message = sprintf('Error parsing line \""%s"\" "%s" file.',$line, $path);
parent::__construct($path, $message, $code, $previous);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace AppBundle\Exception;
use Throwable;
class FileException extends \Exception
{
protected $path;
public function __construct($path, $message = '', $code = 0, Throwable $previous = null)
{
$this->path = $path;
parent::__construct($message, $code, $previous);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace AppBundle\Exception;
use Throwable;
class FileNotFoundException extends FileException
{
public function __construct($path, $message = '', $code = 0, Throwable $previous = null)
{
$message = sprintf('File "%s" not found.', $path);
parent::__construct($path, $message, $code, $previous);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace AppBundle\Exception;
use Throwable;
class ParseErrorException extends FileException
{
public function __construct($path, $message = '', $code = 0, Throwable $previous = null)
{
$message = sprintf('Error parsing "%s" file.', $path);
parent::__construct($path, $message, $code, $previous);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace AppBundle\Exception;
use Throwable;
class WriteErrorException extends FileException
{
public function __construct($file, $message = '', $code = 0, Throwable $previous = null)
{
$message = sprintf('Could not write into "%s" file.', $file);
parent::__construct($file, $message, $code, $previous);
}
}

View File

@ -1,9 +1,9 @@
<?php <?php
namespace AppBundle\Form\Filter; namespace AppBundle\Form\Filter\Model;
use AppBundle\Entity\LDraw\Category; use AppBundle\Entity\LDraw\Category;
use AppBundle\Manager\LDraw\CategoryManager; use AppBundle\Repository\LDraw\CategoryRepository;
use Lexik\Bundle\FormFilterBundle\Filter\Form\Type as Filters; use Lexik\Bundle\FormFilterBundle\Filter\Form\Type as Filters;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
@ -11,17 +11,17 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class CategoryFilterType extends AbstractType class CategoryFilterType extends AbstractType
{ {
private $categoryManager; private $categoryRepository;
public function __construct(CategoryManager $categoryManager) public function __construct(CategoryRepository $categoryRepository)
{ {
$this->categoryManager = $categoryManager; $this->categoryRepository = $categoryRepository;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder->add('id', Filters\ChoiceFilterType::class, [ $builder->add('id', Filters\ChoiceFilterType::class, [
'choices' => $this->categoryManager->findAll(), 'choices' => $this->categoryRepository->findAll(),
'choice_label' => 'name', 'choice_label' => 'name',
'label' => 'filter.part.category', 'label' => 'filter.part.category',
]); ]);

View File

@ -1,6 +1,6 @@
<?php <?php
namespace AppBundle\Form\Filter; namespace AppBundle\Form\Filter\Model;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Lexik\Bundle\FormFilterBundle\Filter\FilterBuilderExecuterInterface; use Lexik\Bundle\FormFilterBundle\Filter\FilterBuilderExecuterInterface;

View File

@ -0,0 +1,58 @@
<?php
namespace AppBundle\Form\Filter\Set;
use Doctrine\ORM\QueryBuilder;
use Lexik\Bundle\FormFilterBundle\Filter\FilterBuilderExecuterInterface;
use Lexik\Bundle\FormFilterBundle\Filter\Form\Type as Filters;
use Lexik\Bundle\FormFilterBundle\Filter\Query\QueryInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SetFilterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('search', Filters\TextFilterType::class, [
'apply_filter' => [$this, 'setSearchCallback'],
'label' => 'filter.part.search',
]);
$builder->add('theme', ThemeFilterType::class, [
'add_shared' => function (FilterBuilderExecuterInterface $builderExecuter) {
$builderExecuter->addOnce($builderExecuter->getAlias().'.theme', 'c', function (QueryBuilder $filterBuilder, $alias, $joinAlias, $expr) {
$filterBuilder->leftJoin($alias.'.theme', $joinAlias);
});
},
]);
}
public function getBlockPrefix()
{
return 'model_filter';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
'validation_groups' => ['filtering'], // avoid NotBlank() constraint-related message
]);
}
public function setSearchCallback(QueryInterface $filterQuery, $field, $values)
{
if (empty($values['value'])) {
return null;
}
// expression that represent the condition
$expression = $filterQuery->getExpr()->orX(
$filterQuery->getExpr()->like('s.name', ':value'),
$filterQuery->getExpr()->like('s.number', ':value')
);
return $filterQuery->createCondition($expression, ['value' => '%'.$values['value'].'%']);
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace AppBundle\Form\Filter\Set;
use AppBundle\Entity\Rebrickable\Theme;
use AppBundle\Repository\Rebrickable\ThemeRepository;
use Lexik\Bundle\FormFilterBundle\Filter\Form\Type as Filters;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ThemeFilterType extends AbstractType
{
private $themeRepository;
public function __construct(ThemeRepository $themeRepository)
{
$this->themeRepository = $themeRepository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', Filters\ChoiceFilterType::class, [
'choices' => $this->themeRepository->findAll(),
'choice_label' => function ($theme, $currentChoiceKey) {
if ($parent = $theme->getParent()) {
if ($parentParent = $parent->getParent()) {
if ($parentParentParent = $parentParent->getParent()) {
return $parentParentParent->getName().' > '.$parentParent->getName().' > '.$parent->getName().' > '.$theme->getName();
}
return $parentParent->getName().' > '.$parent->getName().' > '.$theme->getName();
}
return $parent->getName().' > '.$theme->getName();
}
return $theme->getName();
},
'label' => 'filter.set.theme',
]);
}
public function getParent()
{
return Filters\SharedableFilterType::class; // this allow us to use the "add_shared" option
}
public function getBlockPrefix()
{
return 'theme_filter';
}
public function setDefaultOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Theme::class,
'csrf_protection' => false,
'validation_groups' => ['filtering'], // avoid NotBlank() constraint-related message
'method' => 'GET',
]);
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace AppBundle\Manager;
class BaseManager
{
protected $repository;
/**
* @return mixed
*/
public function getRepository()
{
return $this->repository;
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace AppBundle\Manager\LDraw;
use AppBundle\Entity\LDraw\Alias;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Manager\BaseManager;
use AppBundle\Repository\LDraw\AliasRepository;
class AliasManager extends BaseManager
{
/**
* AliasManager constructor.
*
* @param AliasRepository $repository
*/
public function __construct(AliasRepository $repository)
{
$this->repository = $repository;
}
/**
* Create new Alias entity.
*
* @param $number
* @param Model $model
*
* @return Alias
*/
public function create($number, $model)
{
if (($alias = $this->repository->findOneBy(['number' => $number, 'model' => $model])) == null) {
$alias = new Alias();
$alias->setModel($model);
$alias->setNumber($number);
}
return $alias;
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace AppBundle\Manager\LDraw;
use AppBundle\Entity\LDraw\Category;
use AppBundle\Manager\BaseManager;
use AppBundle\Repository\LDraw\CategoryRepository;
class CategoryManager extends BaseManager
{
/**
* CategoryManager constructor.
*
* @param CategoryRepository $repository
*/
public function __construct(CategoryRepository $repository)
{
$this->repository = $repository;
}
public function findAll()
{
return $this->repository->findAll();
}
/**
* Create new Category entity with $name or retrieve one.
*
* @param $name
*
* @return Category
*/
public function create($name)
{
if (($category = $this->repository->findByName($name)) == null) {
$category = new Category();
$category->setName($name);
}
return $category;
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace AppBundle\Manager\LDraw;
use AppBundle\Entity\LDraw\Keyword;
use AppBundle\Manager\BaseManager;
use AppBundle\Repository\LDraw\KeywordRepository;
class KeywordManager extends BaseManager
{
/**
* KeywordManager constructor.
*
* @param KeywordRepository $repository
*/
public function __construct(KeywordRepository $repository)
{
$this->repository = $repository;
}
/**
* Create new Keyword entity with $name or retrieve one.
*
* @param $name
*
* @return Keyword
*/
public function create($name)
{
if (($keyword = $this->repository->findByName($name)) == null) {
$keyword = new Keyword();
$keyword->setName($name);
}
return $keyword;
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace AppBundle\Manager\LDraw;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Manager\BaseManager;
use AppBundle\Repository\LDraw\ModelRepository;
class ModelManager extends BaseManager
{
/**
* ModelManager constructor.
*
* @param ModelRepository $repository
*/
public function __construct(ModelRepository $repository)
{
$this->repository = $repository;
}
/**
* Create new Model entity with $number or retrieve one.
*
* @param $number
*
* @return Model
*/
public function create($number)
{
if (($model = $this->repository->findOneBy(['number' => $number])) == null) {
$model = new Model();
$model->setNumber($number);
}
return $model;
}
public function findByNumber($number)
{
return $this->repository->findOneByNumber($number);
}
public function findByName($name)
{
return $this->repository->findOneBy(['name' => $name]);
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace AppBundle\Manager\LDraw;
use AppBundle\Entity\LDraw\Subpart;
use AppBundle\Manager\BaseManager;
use AppBundle\Repository\LDraw\SubpartRepository;
class SubpartManager extends BaseManager
{
/**
* SubpartManager constructor.
*
* @param SubpartRepository $repository
*/
public function __construct(SubpartRepository $repository)
{
$this->repository = $repository;
}
/**
* Create new Subpart relation entity or retrieve one by foreign keys.
*
* @param $name
*
* @return Subpart
*/
public function create($parent, $child)
{
if (($subpart = $this->repository->findOneByKeys($parent, $child))) {
$subpart->setCount($subpart->getCount() + 1);
} else {
$subpart = new Subpart();
$subpart
->setParent($parent)
->setSubpart($child)
->setCount(1);
}
return $subpart;
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace AppBundle\Manager\LDraw;
use AppBundle\Entity\LDraw\Type;
use AppBundle\Manager\BaseManager;
use AppBundle\Repository\LDraw\TypeRepository;
class TypeManager extends BaseManager
{
/**
* TypeManager constructor.
*
* @param TypeRepository $typeRepository
*/
public function __construct(TypeRepository $repository)
{
$this->repository = $repository;
}
/**
* Create new Keyword entity with $name or retrieve one.
*
* @param $name
*
* @return Type
*/
public function create($name)
{
if (($type = $this->repository->findByName($name)) == null) {
$type = new Type();
$type->setName($name);
}
return $type;
}
}

View File

@ -1,99 +0,0 @@
<?php
namespace AppBundle\Manager;
use AppBundle\Manager\LDraw\AliasManager;
use AppBundle\Manager\LDraw\CategoryManager;
use AppBundle\Manager\LDraw\KeywordManager;
use AppBundle\Manager\LDraw\ModelManager;
use AppBundle\Manager\LDraw\SubpartManager;
use AppBundle\Manager\LDraw\TypeManager;
class LDrawManager
{
/** @var CategoryManager */
private $categoryManager;
/** @var KeywordManager */
private $keywordManager;
/** @var TypeManager */
private $typeManager;
/** @var SubpartManager */
private $subpartManager;
/** @var ModelManager */
private $modelManager;
/** @var AliasManager */
private $aliasManager;
/**
* LDrawService constructor.
*
* @param CategoryManager $categoryManager
* @param KeywordManager $keywordManager
* @param TypeManager $typeManager
* @param SubpartManager $subpartManager
* @param ModelManager $modelManager
* @param AliasManager $aliasManager
*/
public function __construct(CategoryManager $categoryManager, KeywordManager $keywordManager, TypeManager $typeManager, SubpartManager $subpartManager, ModelManager $modelManager, AliasManager $aliasManager)
{
$this->categoryManager = $categoryManager;
$this->keywordManager = $keywordManager;
$this->typeManager = $typeManager;
$this->subpartManager = $subpartManager;
$this->modelManager = $modelManager;
$this->aliasManager = $aliasManager;
}
/**
* @return mixed
*/
public function getCategoryManager()
{
return $this->categoryManager;
}
/**
* @return mixed
*/
public function getKeywordManager()
{
return $this->keywordManager;
}
/**
* @return TypeManager
*/
public function getTypeManager()
{
return $this->typeManager;
}
/**
* @return SubpartManager
*/
public function getSubpartManager()
{
return $this->subpartManager;
}
/**
* @return ModelManager
*/
public function getModelManager()
{
return $this->modelManager;
}
/**
* @return AliasManager
*/
public function getAliasManager()
{
return $this->aliasManager;
}
}

View File

@ -35,7 +35,7 @@ class Builder
]); ]);
$menu->addChild('Models', [ $menu->addChild('Models', [
'route' => 'ldraw_model_index', 'route' => 'model_index',
]); ]);
$menu->addChild('Sets', [ $menu->addChild('Sets', [

View File

@ -2,8 +2,28 @@
namespace AppBundle\Repository\LDraw; namespace AppBundle\Repository\LDraw;
use AppBundle\Entity\LDraw\Alias;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Repository\BaseRepository; use AppBundle\Repository\BaseRepository;
class AliasRepository extends BaseRepository class AliasRepository extends BaseRepository
{ {
/**
* Get existing entity or create new.
*
* @param $number
* @param Model $model
*
* @return Alias
*/
public function getOrCreate($number, $model)
{
if (($alias = $this->findOneBy(['number' => $number, 'model' => $model])) == null) {
$alias = new Alias();
$alias->setModel($model);
$alias->setNumber($number);
}
return $alias;
}
} }

View File

@ -0,0 +1,31 @@
<?php
namespace AppBundle\Repository\LDraw;
use AppBundle\Entity\LDraw\Author;
use AppBundle\Repository\BaseRepository;
class AuthorRepository extends BaseRepository
{
public function findOneByName($name)
{
return $this->findOneBy(['name' => $name]);
}
/**
* Get existing entity or create new.
*
* @param $name
*
* @return Author
*/
public function getOrCreate($name)
{
if (($author = $this->findOneByName($name)) == null) {
$author = new Author();
$author->setName($name);
}
return $author;
}
}

View File

@ -2,6 +2,7 @@
namespace AppBundle\Repository\LDraw; namespace AppBundle\Repository\LDraw;
use AppBundle\Entity\LDraw\Category;
use AppBundle\Repository\BaseRepository; use AppBundle\Repository\BaseRepository;
class CategoryRepository extends BaseRepository class CategoryRepository extends BaseRepository
@ -10,4 +11,21 @@ class CategoryRepository extends BaseRepository
{ {
return $this->findOneBy(['name' => $name]); return $this->findOneBy(['name' => $name]);
} }
/**
* Get existing entity or create new.
*
* @param $name
*
* @return Category
*/
public function getOrCreate($name)
{
if (($category = $this->findByName($name)) == null) {
$category = new Category();
$category->setName($name);
}
return $category;
}
} }

View File

@ -2,6 +2,7 @@
namespace AppBundle\Repository\LDraw; namespace AppBundle\Repository\LDraw;
use AppBundle\Entity\LDraw\Keyword;
use AppBundle\Repository\BaseRepository; use AppBundle\Repository\BaseRepository;
class KeywordRepository extends BaseRepository class KeywordRepository extends BaseRepository
@ -10,4 +11,21 @@ class KeywordRepository extends BaseRepository
{ {
return $this->findOneBy(['name' => $name]); return $this->findOneBy(['name' => $name]);
} }
/**
* Create new Keyword entity with $name or retrieve one.
*
* @param $name
*
* @return Keyword
*/
public function getOrCreate($name)
{
if (($keyword = $this->findByName($name)) == null) {
$keyword = new Keyword();
$keyword->setName($name);
}
return $keyword;
}
} }

View File

@ -2,30 +2,33 @@
namespace AppBundle\Repository\LDraw; namespace AppBundle\Repository\LDraw;
use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Entity\LDraw\Alias; use AppBundle\Entity\LDraw\Alias;
use AppBundle\Entity\LDraw\Type; use AppBundle\Entity\LDraw\Category;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\LDraw\Subpart;
use AppBundle\Entity\Rebrickable\Inventory; use AppBundle\Entity\Rebrickable\Inventory;
use AppBundle\Entity\Rebrickable\Inventory_Part; use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Repository\BaseRepository; use AppBundle\Repository\BaseRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
class ModelRepository extends BaseRepository class ModelRepository extends BaseRepository
{ {
public function findAllByType($type) public function getFilteredQueryBuilder()
{ {
$queryBuilder = $this->createQueryBuilder('model') $queryBuilder = $this->createQueryBuilder('model')
->join(Type::class, 'type', Join::LEFT_JOIN, 'model.type = :type') // ->where('model.name NOT LIKE :obsolete')
->setParameter('type', $type); // ->setParameter('obsolete','~%')
;
return $queryBuilder->getQuery(); return $queryBuilder;
} }
public function findAllByCategory($category) public function findAllByCategory($category)
{ {
$queryBuilder = $this->createQueryBuilder('model') $queryBuilder = $this->createQueryBuilder('model')
->join(Type::class, 'type', Join::LEFT_JOIN, 'model.category = :category') ->join(Category::class, 'type', Join::LEFT_JOIN, 'model.category = :category')
->setParameter('category', $category); ->setParameter('category', $category);
return $queryBuilder->getQuery(); return $queryBuilder->getQuery();
@ -49,6 +52,11 @@ class ModelRepository extends BaseRepository
return $model; return $model;
} }
public function findOneByName($name)
{
return $this->findOneBy(['name' => $name]);
}
public function findAllBySetNumber($number) public function findAllBySetNumber($number)
{ {
$queryBuilder = $this->createQueryBuilder('model'); $queryBuilder = $this->createQueryBuilder('model');
@ -64,4 +72,38 @@ class ModelRepository extends BaseRepository
return $queryBuilder->getQuery()->getResult(); return $queryBuilder->getQuery()->getResult();
} }
public function findAllRelatedModels($number)
{
$queryBuilder = $this->createQueryBuilder('model');
$queryBuilder
->select('related')
->join(Subpart::class, 'subpart', JOIN::WITH, 'model.number = subpart.subpart')
->join(Subpart::class, 'parent', JOIN::WITH, 'subpart.parent = parent.parent')
->join(Model::class, 'related', JOIN::WITH, 'related.number = parent.subpart')
->where('model.number = :number')
->setParameter('number', $number)
->andWhere('related.number != :number')
->distinct(true);
return $queryBuilder->getQuery()->getResult();
}
/**
* Create new Model entity with $number or retrieve one.
*
* @param $number
*
* @return Model
*/
public function getOrCreate($number)
{
if (($model = $this->findOneBy(['number' => $number])) == null) {
$model = new Model();
$model->setNumber($number);
}
return $model;
}
} }

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